package com.xbongbong.pro.service.toolbox.statistic.help;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.PaasStageEntity;
import com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.elasticsearch.constant.EsNormalConstant;
import com.xbongbong.paas.elasticsearch.pojo.XbbAggregatedPage;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.elasticsearch.util.XbbElasticsearchRestTemplate;
import com.xbongbong.paas.enums.ConditionEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.FundAccountFlowTypeEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.OtherExpenseSheetTypeEnum;
import com.xbongbong.paas.enums.OtherIncomeSheetTypeEnum;
import com.xbongbong.paas.enums.PaySheetTypeEnum;
import com.xbongbong.paas.enums.PaymentSheetTypeEnum;
import com.xbongbong.paas.enums.StageTypeEnum;
import com.xbongbong.paas.enums.StarEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.pojo.CallReckonPojo;
import com.xbongbong.paas.pojo.StageFindPojo;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.helper.PageHelper;
import com.xbongbong.paas.toolbox.thread.TransmittableThreadLocalUtil;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.PageHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.constant.CommonConstant;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.customerstatement.pojo.StatementCustomerPojo;
import com.xbongbong.pro.domain.entity.ChartEntity;
import com.xbongbong.pro.enums.DistributorMarkEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.StatisticsServicesErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.performanceset.pojo.PerformanceSetExplainPojo;
import com.xbongbong.pro.service.stage.StageFindStrategy;
import com.xbongbong.pro.service.stage.factory.StageFindFactory;
import com.xbongbong.pro.statistic.constant.ChartI18Constant;
import com.xbongbong.pro.statistic.enums.IndicatorRuleEnum;
import com.xbongbong.pro.statistic.enums.ResultFromEnum;
import com.xbongbong.pro.statistic.enums.SystemCodeEnum;
import com.xbongbong.pro.statistic.enums.ValueTypeEnum;
import com.xbongbong.pro.statistic.model.ChartModel;
import com.xbongbong.pro.statistic.result.pojo.ChartFindSheetPojo;
import com.xbongbong.pro.statistic.result.pojo.ChartResultBasePojo;
import com.xbongbong.pro.statistic.result.pojo.ChartResultPkPojo;
import com.xbongbong.pro.statistic.result.pojo.ChartResultPojo;
import com.xbongbong.pro.statistic.result.pojo.CustomerOkFirstSecondPojo;
import com.xbongbong.pro.statistic.result.pojo.FloorAggsKeyPojo;
import com.xbongbong.pro.statistic.result.pojo.PanoramaPojo;
import com.xbongbong.pro.statistic.through.pojo.ResultThroughPojo;
import com.xbongbong.pro.statistic.through.pojo.vo.ResultThroughVO;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.PageConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.constant.workorder.WorkOrderConstant;
import com.xbongbong.saas.domain.entity.ClueStageEntity;
import com.xbongbong.saas.domain.entity.ContractPerformanceEntity;
import com.xbongbong.saas.domain.entity.CustomerStageEntity;
import com.xbongbong.saas.domain.entity.SaleStageEntity;
import com.xbongbong.saas.domain.entity.StaticForMarketActivityEntity;
import com.xbongbong.saas.domain.entity.ext.CustomerEntityExt;
import com.xbongbong.saas.enums.BirthdayFilterEnum;
import com.xbongbong.saas.enums.CallCenterCallLogCallSourceEnum;
import com.xbongbong.saas.enums.CustomerStatementEnum;
import com.xbongbong.saas.enums.FundAccountFlowEnum;
import com.xbongbong.saas.enums.OutstockTypeEnum;
import com.xbongbong.saas.enums.SheetAloneEnum;
import com.xbongbong.saas.enums.WorkOrderForStatisticsEnum;
import com.xbongbong.saas.enums.WorkOrderStatusEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ClueEnum;
import com.xbongbong.saas.enums.business.CommunicatePlanEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.CustomerCommunicateEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.InstockEnum;
import com.xbongbong.saas.enums.business.InvoiceEnum;
import com.xbongbong.saas.enums.business.MarketActivityEnum;
import com.xbongbong.saas.enums.business.OrderEnum;
import com.xbongbong.saas.enums.business.OtherExpenseEnum;
import com.xbongbong.saas.enums.business.OtherIncomeEnum;
import com.xbongbong.saas.enums.business.OutstockEnum;
import com.xbongbong.saas.enums.business.PayPlanEnum;
import com.xbongbong.saas.enums.business.PaySheetEnum;
import com.xbongbong.saas.enums.business.PaymentEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.PaymentTaskEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.business.ProductionOrderEnum;
import com.xbongbong.saas.enums.business.PurchaseEnum;
import com.xbongbong.saas.enums.business.PurchaseInvoiceEnum;
import com.xbongbong.saas.enums.business.QuotationEnum;
import com.xbongbong.saas.enums.business.RefundEnum;
import com.xbongbong.saas.enums.business.ReturnEnum;
import com.xbongbong.saas.enums.business.ReturnedPurchaseEnum;
import com.xbongbong.saas.enums.business.SalesOpportunityEnum;
import com.xbongbong.saas.enums.business.WorkOrderEnum;
import com.xbongbong.saas.enums.dictionary.ClueStatusEnum;
import com.xbongbong.saas.enums.dictionary.CommunicatePlanStatusEnum;
import com.xbongbong.saas.enums.dictionary.CustomerTypeEnum;
import com.xbongbong.saas.enums.dictionary.DictionaryEnum;
import com.xbongbong.saas.enums.dictionary.OrderStatusEnum;
import com.xbongbong.saas.enums.dictionary.PayPlanStatusEnum;
import com.xbongbong.saas.enums.dictionary.PaymentStatusEnum;
import com.xbongbong.saas.enums.dictionary.PaymentTaskStatusEnum;
import com.xbongbong.saas.enums.dictionary.ProductionOrderStatusEnum;
import com.xbongbong.saas.enums.dictionary.QuotationStatusEnum;
import com.xbongbong.saas.enums.subform.ContractPerformanceSubFormEnum;
import com.xbongbong.saas.enums.subform.ContractProductSubFormEnum;
import com.xbongbong.saas.enums.subform.SubFormTransferEnum;
import com.xbongbong.saas.model.ClueStageModel;
import com.xbongbong.saas.model.ContractPerformanceModel;
import com.xbongbong.saas.model.CustomerStageLogModel;
import com.xbongbong.saas.model.CustomerStageModel;
import com.xbongbong.saas.model.DataDictionaryModel;
import com.xbongbong.saas.model.IndexTypeModel;
import com.xbongbong.saas.model.OpportunityStageLogModel;
import com.xbongbong.saas.model.SaleStageModel;
import com.xbongbong.saas.service.toolbox.help.SaasListDataPermissionHelp;
import com.xbongbong.saas.service.toolbox.help.ScriptHelp;
import com.xbongbong.saas.service.toolbox.statistic.help.ChartHelp;
import com.xbongbong.sys.model.UserModel;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.join.query.HasChildQueryBuilder;
import org.elasticsearch.join.query.HasParentQueryBuilder;
import org.elasticsearch.join.query.JoinQueryBuilders;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.xbongbong.paas.elasticsearch.constant.EsNormalConstant.MINI_MUM_SHOULD_MATCH;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.elasticsearch.index.query.QueryBuilders.scriptQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * 用于图表查询条件的封装处理
 * @author zcp
 * @version v1.0
 * @date 2019/4/5 14:50
 * @since v1.0
 */
@Component
public class ChartQueryHelp {
    private static final Logger LOG = LoggerFactory.getLogger(ChartQueryHelp.class);
    @Resource
    private ChartDtoHelp chartDtoHelp;
    @Resource
    private EsHelper esHelper;
    @Resource
    private FormHelp formHelp;
    @Resource
    private SystemChartHelp systemChartHelp;
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private PerformanceHelp performanceHelp;
    @Resource
    private DataDictionaryModel dataDictionaryModel;
    @Resource
    private SaleStageModel saleStageModel;
    @Resource
    private CustomerStageModel customerStageModel;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private ChartModel chartModel;
    @Resource
    private UserModel userModel;
    @Resource
    private OpportunityStageLogModel opportunityStageLogModel;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private CustomerStageLogModel customerStageLogModel;
    @Resource
    private XbbElasticsearchRestTemplate xbbElasticsearchRestTemplate;
    @Resource
    private ContractPerformanceModel contractPerformanceModel;
    @Resource
    private ClueStageModel clueStageModel;
    @Resource
    private ChartHelp chartHelp;
    @Resource
    private SaasListDataPermissionHelp saasListDataPermissionHelp;
    @Resource
    private IndexTypeModel indexTypeModel;

    //-------------------------------所有业务基础查询条件：corpid、del、时间-------------------------------
    /**
     * 最基础的查询条件封装，只限制corpid和del
     * @param corpid 公司id
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/4/5 16:46
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4Base(String corpid){
        return boolQueryBuilder4Base(corpid, DelEnum.NORMAL.getDel());
    }
    /**
     * 最基础的查询条件封装，只限制corpid和del（del为null时不限制）
     * @param corpid 公司id
     * @param del 删除标记@see DelEnum，为空时不限制删除条件
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/5/13 21:57
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4Base(String corpid, Integer del){
        return chartHelp.boolQueryBuilder4Base(corpid, del);
    }
    /**
     * 基础的查询条件封装，限制corpid和del，及主键id
     * @param corpid 公司id
     * @param del 如果del为null，则不限制del条件
     * @param dataIdIn 主键id合集
     * @param indexTypeEnum
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/4/9 21:18
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4BaseByIds(String corpid, Integer del, Collection<Long> dataIdIn, IndexTypeEnum indexTypeEnum){
        dataIdIn.add(-1L);
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        if (del != null) {
            boolQueryBuilder.filter(termQuery("del", del));
        }
        boolQueryBuilder.filter(termsQuery(SubFormTransferEnum.getEsAttrByIndexTypeAndDbAttr(indexTypeEnum, "dataId"), dataIdIn));
        return boolQueryBuilder;
    }
    /**
     * 生成基础查询条件：时间范围（限制开始和结束时间）、公司id、del
     * @param chartResultBasePojo 基础参数pojo
     * @param fieldTime 时间筛选字段
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/29 13:55
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4LimitStart(ChartResultBasePojo chartResultBasePojo, String fieldTime){
        //默认限制开始时间
        return boolQueryBuilder4Time(chartResultBasePojo, fieldTime, true);
    }
    /**
     * 生成基础查询条件：时间范围（根据limitStart控制是否限制开始时间）、公司id、del
     * 部分统计场景不限制开始时间，如客户画像等
     * @param chartResultBasePojo 基础参数pojo
     * @param fieldTime 时间筛选字段
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/30 17:02
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4Time(ChartResultBasePojo chartResultBasePojo, String fieldTime, boolean limitStart){
        Integer startTime = chartResultBasePojo.getStartTime();
        Integer endTime = chartResultBasePojo.getEndTime();
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(chartResultBasePojo.getCorpid());
        //时间范围
        RangeQueryBuilder rangeQueryBuilder = rangeQuery(fieldTime);
        if (limitStart) {
            //如果该值为true，则限制开始时间；如果该值为fale，则不限制开始时间。部分特殊场景不限制开始时间，如销售机会区域分布统计等
            rangeQueryBuilder.gte(startTime);
        }
        //TODO 如果截止时间没传，默认给当前时间
        if(endTime == null){
            endTime = DateUtil.getInt();
        }
        boolQueryBuilder.filter(rangeQueryBuilder.lt(endTime));
        return boolQueryBuilder;
    }
    /**
     * 生成基础查询条件：限制创建人（表本身的创建人）、时间范围、公司id、del
     * @param chartResultBasePojo 基础参数pojo
     * @param fieldTime 时间筛选字段
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/5/6 14:27
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4Create(ChartResultBasePojo chartResultBasePojo, String fieldTime){
        //限制开始结束时间
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultBasePojo, fieldTime);
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            //表本身的createId不能变更，因此在data字典内增加创建人key
            String fieldGroup = "creatorId.keyword";
            boolQueryBuilder.filter(termsQuery(fieldGroup, chartResultBasePojo.getUserIdIn()));
        }
        return boolQueryBuilder;
    }

    /**
     * 生成基础查询条件：formId、公司id、del、时间范围（根据limitStart控制是否限制开始时间）
     * @param chartResultBasePojo 基础参数pojo
     * @param fieldTime 筛选时间字段，如创建时间、最后联系时间
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws
     * @author zcp
     * @date 2019/4/5 16:52
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4Form(ChartResultBasePojo chartResultBasePojo, String fieldTime, boolean limitStart){
        Long formId = chartResultBasePojo.getRefId();
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Time(chartResultBasePojo, fieldTime, limitStart);
        if (formId != null && formId > 0) {
            boolQueryBuilder.filter(termQuery("formId", formId));
        }
        return boolQueryBuilder;
    }

    //-------------------------------客户相关查询条件封装-------------------------------
    /**
     * 按照负责人统计客户的查询基本条件：公司id、del、创建时间、表单id、团队主负责人
     * 注：该方法限制了主负责人
     * @param chartResultPojo 参数pojo
     * @param fieldTime 时间字段
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/21 14:53
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder customerQuery4Team(ChartResultPojo chartResultPojo, String fieldTime, boolean limitStart){
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Form(chartResultPojo, fieldTime, limitStart);
        customerTeam(chartResultPojo, boolQueryBuilder);
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    public BoolQueryBuilder orderQuery4Team(ChartResultPojo chartResultPojo, String fieldTime, boolean limitStart) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Time(chartResultPojo, fieldTime, limitStart);
        // 该表单的数据权限
        TransmittableThreadLocalUtil.setValue(SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.ORDER.getCode(), null, DistributorMarkEnum.DISTRIBUTOR.getCode());
        List<ConditionsEntityExt> conditionsList = new ArrayList<>();
        saasListDataPermissionHelp.listDataPermission(conditionsList,chartResultPojo.getLoginUser(), XbbRefTypeEnum.ORDER.getCode(), XbbRefTypeEnum.ORDER.getCode());
        if (CollectionUtils.isNotEmpty(conditionsList)){
            EsUtil.parseCondition(boolQueryBuilder,conditionsList,"",chartResultPojo.getCorpid(),XbbRefTypeEnum.ORDER.getCode());
        }
        //无需审批和通过  订货单不进审批流
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    public BoolQueryBuilder returnQuery4Team(ChartResultPojo chartResultPojo, String fieldTime, boolean limitStart) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Time(chartResultPojo, fieldTime, limitStart);
        returnTeam(chartResultPojo, boolQueryBuilder);
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }
    /**
     * 新增客户查询基本条件：公司id、del（新增客户包含回收站客户）、创建时间、表单id、客户创建人
     * 注：该方法限制了创建人
     * @param userFlag 是否在查询时限制签订人
     * @param chartResultBasePojo 参数pojo
     * @param fieldTime 时间字段
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/30 19:04
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder customerQuery4Create(boolean userFlag, ChartResultBasePojo chartResultBasePojo, String fieldTime){
        //新增客户都限制开始时间
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Form(chartResultBasePojo, fieldTime, true);
        //移除del，重新塞入新的条件：新增客户要包含回收站
        esHelper.removeSearchCondition(boolQueryBuilder, "del");
        boolQueryBuilder.filter(termsQuery("del", Arrays.asList(DelEnum.NORMAL.getDel(), DelEnum.DELETE.getDel())));
        //限制创建人id、且非主管或筛选
        if (userFlag && chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            //表本身的createId不能变更，因此在data字典内增加创建人key
            String fieldGroup = CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.CREATOR);
            boolQueryBuilder.filter(termsQuery(fieldGroup, chartResultBasePojo.getUserIdIn()));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 封装成交客户需要的关键条件：公司id，del、签订人、合同状态
     * @param chartResultPojo 参数pojo
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间（区域分布）
     * @return org.elasticsearch.index.query.BoolQueryBuilder 返回es查询用boolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/2/22 22:09
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder customerOkQuery(ChartResultPojo chartResultPojo, boolean limitStart) throws XbbException {
        //限制签订人、非红冲、签订时间（是否限制根据limitStart）
        BoolQueryBuilder boolQueryBuilder = contractQuery4Base(true, 0, chartResultPojo, limitStart);
        return boolQueryBuilder;
    }

    /**
     * 从合同中获取成交的客户id
     * @param chartResultPojo
     * @param boolQueryBuilder
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间（区域分布）
     * @throws XbbException
     * @author zcp
     * @date 2019/2/24 18:12
     * @since v1.0
     * @version v1.0
     */
    public void getDealCustomerIdfromContract(ChartResultPojo chartResultPojo, BoolQueryBuilder boolQueryBuilder, boolean limitStart) throws XbbException {
        /*--------处理查询条件，不限制开始时间--------*/
        BoolQueryBuilder contractQueryBuilder = customerOkQuery(chartResultPojo, limitStart);
        //移除formId，客户画像不限制合同的formId
        esHelper.removeSearchCondition(contractQueryBuilder, "formId");
        //时间聚合字段
        String fieldTime = ContractEnum.getEsAttr4Keyword(ContractEnum.SIGN_TIME);
        //排重字段
        String fieldGroup = ContractEnum.getEsAttr4Keyword(ContractEnum.LINK_CUSTOMER);
        FloorAggsKeyPojo floorAggsKeyPojo = systemChartHelp.getFloorAggsKey(chartResultPojo, IndexTypeEnum.IDX_SAAS_CONTRACT, contractQueryBuilder, fieldTime, fieldGroup, true);
        Set totalSet = floorAggsKeyPojo.getTotalSet();
        if (totalSet.size() == 0) {
            totalSet.add(-1);
        }
        //客户id塞入boolQueryBuilder
        boolQueryBuilder.filter(termsQuery("dataId", totalSet));
        //移除addTime，成交客户画像限制合同签订时间，不限制客户创建时间
        esHelper.removeSearchCondition(boolQueryBuilder, "addTime");
        esHelper.removeSearchCondition(boolQueryBuilder, "addTime");
    }

    /**
     * 从跟进记录中获取跟进的id，如客户id、跟进机会id等
     * 注：当非客户id时，需要制定跟进记录关联的业务类型
     * @param xbbRefTypeEnum 跟进记录关联的业务
     * @param chartResultPojo 参数pojo
     * @param boolQueryBuilder 查询条件
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间（区域分布）
     * @author zcp
     * @date 2019/2/25 19:28
     * @since v1.0
     * @version v1.0
     */
    public void getFollowIdfromCommunicate(XbbRefTypeEnum xbbRefTypeEnum, ChartResultPojo chartResultPojo, BoolQueryBuilder boolQueryBuilder, boolean limitStart) throws XbbException {
        /*--------处理查询条件，限制创建人、限制开始时间通过limitStart控制--------*/
        BoolQueryBuilder communicateQueryBuilder = communicateQuery4Base(true, chartResultPojo, limitStart);
        if(!Objects.equals(xbbRefTypeEnum.getCode(),XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())){
            //限制业务类型
            String communicateBusinessType = CustomerCommunicateEnum.getEsAttr4Keyword(CustomerCommunicateEnum.COMMUNICATE_BUSINESS_TYPE);
            int businessType = xbbRefTypeEnum.getCode();
            communicateQueryBuilder.filter(termQuery(communicateBusinessType, businessType));
        }
        TermsAggregationBuilder agg = AggregationBuilders.terms("aggs01").field("data." + CustomerCommunicateEnum.CUSTOMER_NAME.getAttr()+".keyword").size(Integer.MAX_VALUE);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_CUSTOMER_COMMUNICATE.getIndex());
        communicateQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_CUSTOMER_COMMUNICATE.getType()));
        SearchSourceBuilder communicateSourceBuilder = new SearchSourceBuilder();
        communicateSourceBuilder.query(communicateQueryBuilder);
        communicateSourceBuilder.from(0);
        communicateSourceBuilder.size(0);
        if (!Objects.equals(XbbRefTypeEnum.CLUE, xbbRefTypeEnum)) {
            agg = AggregationBuilders.terms("aggs01").field("data." + CustomerCommunicateEnum.CUSTOMER_NAME.getAttr()+".keyword").size(Integer.MAX_VALUE);
            communicateSourceBuilder.fetchSource("data."+ CustomerCommunicateEnum.CUSTOMER_NAME.getAttr(), null);
        } else {
            agg = AggregationBuilders.terms("aggs01").field("data." + CustomerCommunicateEnum.COMMUNICATE_BUSINESS.getAttr()+".keyword").size(Integer.MAX_VALUE);
        }
        agg.executionHint(ElasticConstant.EXECUTION_HINT);
        communicateSourceBuilder.aggregation(agg);
        searchRequest.source(communicateSourceBuilder);
        JSONArray resultArray = xbbElasticsearchRestTemplate.getAggResponseJson(searchRequest).getJSONObject("aggregations").getJSONObject("aggs01").getJSONArray("buckets");
        Set<Long> totalSet = new HashSet<>();
        if (CollectionUtils.isNotEmpty(resultArray)) {
            for (int i = 0; i < resultArray.size(); i++) {
                JSONObject object = resultArray.getJSONObject(i);
                if(object != null){
                    totalSet.add(object.getLongValue(StringConstant.KEY));
                }
            }
        }
        if (totalSet.size() == 0) {
            totalSet.add(-1L);
        }
        boolQueryBuilder.filter(termsQuery("dataId", totalSet));
    }

    public void getFollowIdfromCommunicateLimit(XbbRefTypeEnum xbbRefTypeEnum, ChartResultPojo chartResultPojo, BoolQueryBuilder boolQueryBuilder, boolean limitStart) throws XbbException {
        /*--------处理查询条件，限制创建人、限制开始时间通过limitStart控制--------*/
        BoolQueryBuilder communicateQueryBuilder = communicateQuery4Base(true, chartResultPojo, limitStart);
        if(!Objects.equals(xbbRefTypeEnum.getCode(),XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())){
            //限制业务类型
            String communicateBusinessType = CustomerCommunicateEnum.getEsAttr4Keyword(CustomerCommunicateEnum.COMMUNICATE_BUSINESS_TYPE);
            int businessType = xbbRefTypeEnum.getCode();
            communicateQueryBuilder.filter(termQuery(communicateBusinessType, businessType));
        }
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_CUSTOMER_COMMUNICATE.getIndex());
        communicateQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_CUSTOMER_COMMUNICATE.getType()));
        SearchSourceBuilder communicateSourceBuilder = new SearchSourceBuilder();
        communicateSourceBuilder.query(communicateQueryBuilder);
        EsUtil.setPage(communicateSourceBuilder, 1, EsNormalConstant.FIND_NUM);
        communicateSourceBuilder.fetchSource("data."+ CustomerCommunicateEnum.CUSTOMER_NAME.getAttr(), null);
        searchRequest.source(communicateSourceBuilder);
        List<PaasFormDataEntity> resultList = xbbElasticsearchRestTemplate.getScrollListForExport(PaasFormDataEntity.class, searchRequest);
        Set<Long> totalSet = new HashSet<>();
        for (PaasFormDataEntity paasFormDataEntity : resultList) {
            JSONObject data = paasFormDataEntity.getData();
            if(data != null){
                totalSet.add(data.getLongValue(CustomerCommunicateEnum.CUSTOMER_NAME.getAttr()));
            }
        }
        if (totalSet.size() == 0) {
            totalSet.add(-1L);
        }
        boolQueryBuilder.filter(termsQuery("dataId", totalSet));
    }

    /**
     * 流失客户查询条件：按照更新时间、客户状态名为“无效客户”
     * @param chartResultPojo 参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/1 20:05
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder customerQuery4Lose(ChartResultPojo chartResultPojo) throws XbbException {
        //此处用updateTime,因为客户可能是很早添加的，后来转为无效。后续可能会有一个客户状态变更表，用来标记客户状态变更记录
        String fieldTime = "updateTime";
        BoolQueryBuilder boolQueryBuilder = customerQuery4Team(chartResultPojo, fieldTime, true);
        String typeLose = commonHelp.getCustomerType4Lose(chartResultPojo.getCorpid());
        boolQueryBuilder.filter(termQuery(CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.TYPE), typeLose));
        return boolQueryBuilder;
    }

    /**
     * 遗忘客户条件封装
     * @param chartResultPojo 参数条件
     * @param fieldTime 时间字段
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/5/1 16:29
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder customerQuery4Forget(SystemCodeEnum systemCodeEnum, ChartResultPojo chartResultPojo, String fieldTime){
        int now = DateUtil.getInt();
        Integer startTime = chartResultPojo.getStartTime();
        Integer endTime = chartResultPojo.getEndTime();
        switch (systemCodeEnum) {
            //遗忘提醒相关
            case CUSTOMER_FORGET_ONETOTWOWEEKS:
                startTime = now - 2 * TimeConstant.SECONDS_PER_WEEK;
                endTime = now - TimeConstant.SECONDS_PER_WEEK;
                break;
            case CUSTOMER_FORGET_TWOWEEKSTOONEMONTH:
                startTime = now - TimeConstant.SECONDS_PER_MONTH;
                endTime = now - 2 * TimeConstant.SECONDS_PER_WEEK;
                break;
            case CUSTOMER_FORGET_ONETOTWOMONTHS:
                startTime = now - 2 * TimeConstant.SECONDS_PER_MONTH;
                endTime = now - TimeConstant.SECONDS_PER_MONTH;
                break;
            case CUSTOMER_FORGET_MORETHANTWOMONTHS:
                startTime = 1;
                endTime = now - 2 * TimeConstant.SECONDS_PER_MONTH;
                break;
            default:
                break;
        }
        chartResultPojo.setStartTime(startTime);
        chartResultPojo.setEndTime(endTime);
        BoolQueryBuilder boolQueryBuilder = customerQuery4Team(chartResultPojo, fieldTime, true);
        //排除成交客户和无效客户
        List<String> excludeCustomer = Arrays.asList(CustomerTypeEnum.TRANSACTION_CUSTOMER.getCode(), CustomerTypeEnum.INVALID_CUSTOMER.getCode());
        boolQueryBuilder.filter(boolQuery().mustNot(termsQuery(CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.TYPE), excludeCustomer)));
        return boolQueryBuilder;
    }

    /**
     * 客户生日查询参数
     *
     * @param chartResultPojo 入参
     * @param solarStartTime 开始时间
     * @param solarEndTime 结束时间
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/6/15 11:15
     * @since v1.0
     */
    public BoolQueryBuilder customerQuery4Birthday(ChartResultPojo chartResultPojo, Integer solarStartTime, Integer solarEndTime) {
        //corpid,del
        BoolQueryBuilder birthdayQuery = boolQueryBuilder4Base(chartResultPojo.getCorpid());
        //无需审批和通过
        birthdayQuery.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        //客户团队处理
        customerTeam(chartResultPojo, birthdayQuery);
        EsUtil.conditionBirthday(birthdayQuery, BirthdayFilterEnum.TOMORROW.getValue(), null);
        return birthdayQuery;
    }

    /**
     * 客户阶段漏斗统计入参
     *
     * @param chartResultPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/6/15 11:15
     * @since v1.0
     */
    public BoolQueryBuilder customerFunnelQuery(ChartResultPojo chartResultPojo, BoolQueryBuilder boolQueryBuilder, boolean limitStart, String dictionaryCode) throws XbbException{
        String corpid = chartResultPojo.getCorpid();
        //业务模版筛选
        Long formId = chartResultPojo.getRefId();

        Integer startTime = chartResultPojo.getStartTime();
        Integer endTime = chartResultPojo.getEndTime();
        ChartEntity chartEntity = chartResultPojo.getChartEntity();
        //兼容空： 图表的 timeAttr 字段为空的话，则统计规则字段取更新时间，否则取得是 字段的值
        String timeAttr = (Objects.isNull(chartEntity) || Objects.isNull(chartEntity.getTimeAttr())) ? CustomerManagementEnum.UPDATE_TIME.getAttr() : chartEntity.getTimeAttr().getString("attr");
        //如果统计的字段，在客户模板那里是禁用的，那么不查询数据 (流失客户的入参refId 对于的业务是formId)
        if(IsOpenRuleField(corpid,formId,timeAttr)){
            boolQueryBuilder.filter(termQuery("id", -1));
            return boolQueryBuilder;
        }
        // updateTime作为统计维度
        if(CustomerManagementEnum.UPDATE_TIME.getAttr().equals(timeAttr)){
            //时间字段是 更新时间的话 ，统计的是 最新一次阶段跳转的更新时间在这个范围内的客户，这里将它转为符合条件的id
            Map<String ,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("isNow", 1);
            if(Objects.nonNull(dictionaryCode)){
                param.put("stageCode", dictionaryCode);
            }

            //是否开启了时间筛选
            // updateTime作为统计维度(intoTime)
            if (limitStart) {
                param.put("startTime", startTime);
            }
            //其实formId在销售漏斗查询数据时，应该是要必须要传输。但是入参是没有空校验的，加个判空稳一手
            if(Objects.nonNull(formId)){
                param.put("formId",formId);
            }
            param.put("endTime", endTime);
            param.put("del", DelEnum.NORMAL.getDel());
            param.put(StringConstant.COLUMNS, "data_id");
            List<Long> customerIds = customerStageLogModel.getLoseCustomerList(param);
            //如果 customerIds 为空 ,说明没有符合条件的客户，传输-1限制
            if(CollectionUtils.isEmpty(customerIds)){
                boolQueryBuilder.filter(termQuery("id", -1));
            }else {
                boolQueryBuilder.filter(termsQuery("id", customerIds));
            }
        } else {
            //如果是 创建时间 和  预计结束时间 ,或者最后跟进时间date_2的话 ，则走正常的时间筛选(创建时间addTime筛选前面不要加data.前缀)
            String fieldTime = CustomerManagementEnum.ADD_TIME.getAttr().equals(timeAttr) ? timeAttr : "data."+timeAttr;
            RangeQueryBuilder rangeQueryBuilder = rangeQuery(fieldTime);
            if (limitStart) {
                //如果该值为true，则限制开始时间；如果该值为fale，则不限制开始时间。部分特殊场景不限制开始时间，如销售机会区域分布统计等
                rangeQueryBuilder.gte(startTime);
            }
            boolQueryBuilder.filter(rangeQueryBuilder.lt(endTime));
        }
        // 之前图表不支持多人维度，所以默认取了创建人字段，作为临时过渡方案; 现在按照设置的归属人来统计。(没有设置归属人的，就按照“创建人”来统计)
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            //多人维度 查询条件封装
            setCustomerBelongRule(chartResultPojo , boolQueryBuilder);
        }
        return boolQueryBuilder;
    }

    /**
     * 流失客户es查询入参组装
     *
     * @param chartResultPojo
     * @param
     * @param limitStart
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author youli.chen
     * @date 2020/11/6 11:15
     * @since v1.0
     */
    public BoolQueryBuilder customerQueryBaseForLose(ChartResultPojo chartResultPojo, boolean limitStart, String code, boolean fromCode) throws XbbException {
        String corpid = chartResultPojo.getCorpid();
        //业务模版筛选
        Long formId = chartResultPojo.getRefId();

        //时间范围，限制开始结束时间（特殊处理：updateTime）
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(chartResultPojo.getCorpid());
        if (formId != null && formId > 0) {
            boolQueryBuilder.filter(termQuery("formId", formId));
        }

        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));

        Integer startTime = chartResultPojo.getStartTime();
        Integer endTime = chartResultPojo.getEndTime();
        ChartEntity chartEntity = chartResultPojo.getChartEntity();

        //兼容空： 图表的 timeAttr 字段为空的话，则统计规则字段取更新时间，否则取得是 字段的值
        String timeAttr = (Objects.isNull(chartEntity) || Objects.isNull(chartEntity.getTimeAttr())) ? CustomerManagementEnum.UPDATE_TIME.getAttr() : chartEntity.getTimeAttr().getString("attr");
        //如果统计的字段，在客户模板那里是禁用的，那么不查询数据 (流失客户的入参refId 对于的业务是formId)
        if(IsOpenRuleField(corpid,formId,timeAttr)){
            boolQueryBuilder.filter(termQuery("id", -1));
            return boolQueryBuilder;
        }

        //时间字段是 更新时间的话 ，统计的是 最新一次阶段跳转的更新时间在这个范围内的客户，这里将它转为符合条件的id
        Map<String ,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

        if(!CustomerManagementEnum.UPDATE_TIME.getAttr().equals(timeAttr)){
            //如果是 创建时间 ,或者最后跟进时间date_2的话 ，则走正常的时间筛选(创建时间addTime筛选前面不要加data.前缀)
            String fieldTime = CustomerManagementEnum.ADD_TIME.getAttr().equals(timeAttr) ? timeAttr : "data."+timeAttr;
            RangeQueryBuilder rangeQueryBuilder = rangeQuery(fieldTime);
            if (limitStart) {
                //如果该值为true，则限制开始时间；如果该值为fale，则不限制开始时间。部分特殊场景不限制开始时间，如销售机会区域分布统计等
                rangeQueryBuilder.gte(startTime);
            }
            boolQueryBuilder.filter(rangeQueryBuilder.lt(endTime));
        } else {
            //时间范围
            RangeQueryBuilder updateTimeQuery = rangeQuery("updateTime");
            if (limitStart) {
                //如果该值为true，则限制开始时间；如果该值为fale，则不限制开始时间。部分特殊场景不限制开始时间，如销售机会区域分布统计等
                updateTimeQuery.gte(startTime);
            }
            boolQueryBuilder.filter(updateTimeQuery.lt(endTime));
            //是否开启了时间筛选
            // updateTime作为统计维度(intoTime)
            if (limitStart) {
                param.put("startTime", startTime);
            }
            param.put("endTime", endTime);
        }

        param.put("corpid", corpid);
        param.put("isNow", 1);
        param.put("stageType", StageTypeEnum.FAIL_STAGE.getType());
        //其实formId在销售漏斗查询数据时，应该是要必须要传输。但是入参是没有空校验的，加个判空稳一手
        if(Objects.nonNull(formId)){
            param.put("formId",formId);
        }
        if(code != null){
            if(fromCode){
                param.put("reasonId", Integer.valueOf(code));
            } else {
                param.put("beforeStageCode",code);
            }
        }
        param.put("del", DelEnum.NORMAL.getDel());
        param.put(StringConstant.COLUMNS, "data_id");
        List<Long> customerIds = customerStageLogModel.getLoseCustomerList(param);

        Map<String, Object> stageParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        stageParam.put("corpid", corpid);
        if (Objects.nonNull(formId) && formId > 0) {
            stageParam.put("formId", formId);
        }
        stageParam.put("enable", 1);
        stageParam.put("type", StageTypeEnum.FAIL_STAGE.getType());
        stageParam.put("del", 0);
        List<CustomerStageEntity> customerStageEntityList = customerStageModel.findEntitys(stageParam);
        List<String> customerFailStageList = customerStageEntityList.stream().map(CustomerStageEntity::getCode).distinct().collect(Collectors.toList());
        //阶段id
        boolQueryBuilder.filter(termsQuery(CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.CUSTOMER_STAGE), customerFailStageList));

        // 之前图表不支持多人维度，所以默认取了创建人字段，作为临时过渡方案; 现在按照设置的归属人来统计。(没有设置归属人的，就按照“创建人”来统计)
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            //多人维度 查询条件封装
            setCustomerBelongRule(chartResultPojo , boolQueryBuilder);
        }

        // 保险操作
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        IndexTypeEnum customerIndex = indexTypeModel.getCustomerIndex(corpid, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), SaasMarkEnum.SAAS.getCode());

        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, customerIndex.getType()));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(customerIndex.getIndex());
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 1, PaasConstant.ES_MAX_PAGE_SIZE);
        searchRequest.source(sourceBuilder);
        List<String> fieldList = new ArrayList<>();
        fieldList.add(FieldTypeEnum.DATAID.getAlias());
        String[] strings = new String[fieldList.size()];
        sourceBuilder.fetchSource(fieldList.toArray(strings),null);
        XbbAggregatedPage<CustomerEntityExt> customerEntityExtList = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, CustomerEntityExt.class);
        List<CustomerEntityExt> list = customerEntityExtList.getContent();
        if (list.size() == 0) {
            //如果 customerIds 为空 ,说明没有符合条件的客户，传输-1限制
            boolQueryBuilder.filter(termQuery("id", -1));
        } else {
            List<Long> esCustomerList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            list.forEach(customerEntityExt -> esCustomerList.add(customerEntityExt.getDataId()));
            customerIds.retainAll(esCustomerList);
            boolQueryBuilder.filter(termsQuery("id", customerIds));
        }
        return boolQueryBuilder;
    }

    private void setCustomerBelongRule(ChartResultBasePojo chartResultBasePojo, BoolQueryBuilder boolQueryBuilder) throws XbbException {
        ChartEntity chartEntity = chartResultBasePojo.getChartEntity();
        List<String> userIdIn = chartResultBasePojo.getUserIdIn();
        String corpid = chartResultBasePojo.getCorpid();
        //客户这边的 RefId 是模板 Id
        Long formId = chartResultBasePojo.getRefId();
        //图表归属人统计字段为空，或者是创建人维度。
        if(Objects.isNull(chartEntity) || Objects.isNull(chartEntity.getBelongAttr()) || CustomerManagementEnum.CREATOR_ID.getAttr().equals(chartEntity.getBelongAttr().getString("attr"))){
            boolQueryBuilder.filter(termsQuery(CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.CREATOR),userIdIn));//text字段类型需要.keyword
            //如果统计的字段是禁用的，传输-1，那么不查询数据 (eg: 漏斗统计规则的"归属人"是"创建时间"，但是后面对应的机会模板的'创建时间"被禁用了，那么漏斗统计不到数据)
            if(IsOpenRuleField(corpid ,formId,"creatorId")){
                boolQueryBuilder.filter(termQuery("id", -1));
            }
            return;
        }
        //如果统计的字段是禁用的，传输-1，那么不查询数据 (eg: 漏斗统计规则的"归属人"是"创建时间"，但是后面对应的机会模板的'创建时间"被禁用了，那么漏斗统计不到数据)
        String belongAttr = chartEntity.getBelongAttr().getString("attr");
        if(IsOpenRuleField(corpid ,formId,belongAttr)){
            boolQueryBuilder.filter(termQuery("id", -1));
            return;
        }
        //客户上面关联的负责人，协同人的信息是保存在 es的 /bi_idx_saas_customer/my_team ,是属于"/bi_idx_saas_customer/saas_customer"的子级。所以需要关联查询
        //提示： 负责人，协同人对于的数据库是  tb_saas_customer_user 表。统计规则默认先给-1
        Integer rule = -1;
        //负责人 is_main 字段值  为  1
        if(CustomerManagementEnum.OWNER_ID.getAttr().equals(belongAttr)){
            rule = BasicConstant.MAIN_USER;
        }
        //协同人 is_main 字段值  为  0
        if(CustomerManagementEnum.COUSERID.getAttr().equals(belongAttr)){
            rule = BasicConstant.COOP_USER;
        }
        IndexTypeEnum customerTeamIndex = indexTypeModel.getCustomerTeamIndex(corpid, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), SaasMarkEnum.SAAS.getCode());
        HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(customerTeamIndex, chartEntity.getCorpid(), rule , userIdIn);
        boolQueryBuilder.filter(hasChildQueryBuilder);
    }

    //-------------------------------合同相关查询条件封装-------------------------------
    /**
     * 合同相关统计的共有筛选条件：corpid、del、templateId、signTime、signPerson，默认不限制红冲
     *
     * @param chartResultPojo 请求系统指标接口需要用到的pojo类（内部存系统指标统计时需要的参数）
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author 徐俊杰
     * @date 2019/2/28 9:22
     * @since v1.0
     */
    public BoolQueryBuilder contractQuery4SignNoRed(ChartResultPojo chartResultPojo) throws XbbException {
        //默认限制签订人，且不限制红冲
        return contractQuery4Sign(null, chartResultPojo);
    }
    /**
     * 合同相关统计的共有筛选条件：corpid、del、signTime、统计状态、formId、是否红冲、signPerson---限制签订人
     * @param isRed 是否红冲，null不作为筛选条件，1是0否
     * @param chartResultPojo
     * @throws XbbException
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws
     * @author zcp
     * @date 2019/3/1 15:30
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder contractQuery4Sign(Integer isRed, ChartResultPojo chartResultPojo) throws XbbException {
        //塞入基础查询条件：限制签订人
        BoolQueryBuilder boolQueryBuilder = contractQuery4LimitStart(true, isRed, chartResultPojo);
        return boolQueryBuilder;
    }

    /**
     * 合同相关统计的共有筛选条件：corpid、del、signTime、formId、signPerson---限制签订人
     * @param chartResultPojo
     * @throws XbbException
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws
     * @author weiming
     * @date 2020/1/14 15:30
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder  packageOrderSearch(ChartResultPojo chartResultPojo) throws XbbException {
        //TODO 封装search中的经销商名称查询条件
        //塞入基础查询条件：限制签订人
        String fieldTime = "addTime";
        //封装开始时间和结束时间
        BoolQueryBuilder boolQueryBuilder = orderQuery4Team(chartResultPojo, fieldTime, true);
        List<Long> formIdList = paasFormModel.getFormIdList4Distributor(
                XbbRefTypeEnum.CONTRACT.getCode(), chartResultPojo.getCorpid(), 1);
        boolQueryBuilder.filter(termsQuery("formId",formIdList));
        boolQueryBuilder.filter(termQuery(OrderEnum.getAttrConnectData(OrderEnum.IS_RED), 0));
        //订单状态：待发货、待收货、已完成、已关闭。
        boolQueryBuilder.filter(termsQuery(OrderEnum.getAttrConnectData(OrderEnum.STATUS), OrderStatusEnum.getCode4Statistics()));
        List<JSONObject> searchList = chartResultPojo.getSearch();
        if (CollectionsUtil.isNotEmpty(searchList)) {
            for (int i = 0; i < searchList.size(); i++) {
                JSONObject jsonObject = searchList.get(i);
                ConditionsEntityExt condition = JSON.parseObject(jsonObject.toJSONString(), ConditionsEntityExt.class);
                condition.setAttr(OrderEnum.getAttrConnectData(OrderEnum.LINK_DISTRIBUTOR));
                ConditionEnum conditionEnum = ConditionEnum.getConditionEnum(condition.getSymbol());
                EsUtil.symbolChoose4Distributor(boolQueryBuilder, conditionEnum, condition);
            }
        }
        return boolQueryBuilder;
    }

    /**
     * 合同相关统计的共有筛选条件：corpid、del、signTime、formId、signPerson---限制签订人
     * @param chartResultPojo
     * @throws XbbException
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws
     * @author weiming
     * @date 2020/1/14 15:30
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder packageReturnSearch(ChartResultPojo chartResultPojo) throws XbbException {
        String fieldTime = "addTime";
        //封装开始时间和结束时间、签订人
        BoolQueryBuilder boolQueryBuilder = returnQuery4Team(chartResultPojo, fieldTime, true);
        //封装formId和经销商名称
        List<Long> formIdList = paasFormModel.getFormIdList4Distributor(
                XbbRefTypeEnum.REFUND.getCode(), chartResultPojo.getCorpid(), 1);
        boolQueryBuilder.filter(termsQuery("formId",formIdList));
        return boolQueryBuilder;
    }

    /**
     * 合同相关统计的共有筛选条件：corpid、del、signTime、统计状态、formId、是否红冲
     * @param userFlag 是否在查询时限制签订人
     * @param isRed 是否红冲，null不作为筛选条件，1是0否
     * @param chartResultBasePojo 基础参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/3/19 20:45
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder contractQuery4LimitStart(boolean userFlag, Integer isRed, ChartResultBasePojo chartResultBasePojo) throws XbbException {
        //默认限制开始时间
        return contractQuery4Base(userFlag, isRed, chartResultBasePojo, true);
    }
    /**
     * 该方法以父为主，即主要查询合同
     * 开启业绩分配时，时间和组织架构限制规则为：限制合同的签订时间、业绩分配的分配人
     * @param isRed 是否红冲，null不作为筛选条件，1是0否
     * @param userFlag 是否在查询时限制分配人
     * @param chartResultBasePojo 基础参数pojo
     * @return java.util.Set<java.lang.Long>
     * @throws XbbException
     * @author zcp
     * @date 2019/5/2 14:19
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder contractQuery4performance2Parent(Integer isRed, boolean userFlag, ChartResultBasePojo chartResultBasePojo) throws XbbException {
        //开启业绩分配时，不限制合同的签订人
        BoolQueryBuilder contractQueryBuilder = contractQuery4LimitStart(false, isRed, chartResultBasePojo);
        //限制分配人id、且非主管或筛选
        if (userFlag && chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            //es结构内，合同业绩分配为合同的子，限制业绩分配的分配人
            BoolQueryBuilder performanceQueryBuilder = boolQueryBuilder4Base(chartResultBasePojo.getCorpid());
            performanceQueryBuilder.filter(termsQuery(ContractPerformanceSubFormEnum.getEsAttr(ContractPerformanceSubFormEnum.USER_ID), chartResultBasePojo.getUserIdIn()));
            HasChildQueryBuilder hasChildQueryBuilder = JoinQueryBuilders.hasChildQuery(IndexTypeEnum.IDX_SAAS_CONTRACT_PERFORMANCE.getType(), performanceQueryBuilder, ScoreMode.Max);
            contractQueryBuilder.filter(hasChildQueryBuilder);
        }
        return contractQueryBuilder;
    }
    /**
     * 该方法以子为主，即主要查询合同业绩分配
     * 开启业绩分配时，时间和组织架构限制规则为：限制合同的签订时间、业绩分配的分配人
     * @param isRed 是否红冲，null不作为筛选条件，1是0否
     * @param userFlag 是否在查询时限制分配人
     * @param chartResultBasePojo 基础参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/2 15:04
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder contractQuery4performance2Child(Integer isRed, boolean userFlag, ChartResultBasePojo chartResultBasePojo) throws XbbException {
        BoolQueryBuilder performanceQueryBuilder = boolQueryBuilder4Base(chartResultBasePojo.getCorpid());
        //限制分配人id、且非主管或筛选
        if (userFlag && chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            //es结构内，合同业绩分配为合同的子，限制业绩分配的分配人
            performanceQueryBuilder.filter(termsQuery(ContractPerformanceSubFormEnum.getEsAttr(ContractPerformanceSubFormEnum.USER_ID), chartResultBasePojo.getUserIdIn()));
        }
        /**
         开启业绩分配时，不限制合同的签订人
         把合同相关作为父级条件
         */
        BoolQueryBuilder contractQueryBuilder = contractQuery4LimitStart(false, isRed, chartResultBasePojo);
        HasParentQueryBuilder hasParentQueryBuilder = JoinQueryBuilders.hasParentQuery(IndexTypeEnum.IDX_SAAS_CONTRACT.getType(), contractQueryBuilder, false);
        performanceQueryBuilder.filter(hasParentQueryBuilder);
        return performanceQueryBuilder;
    }
    /**
     * 合同业绩分配基础查询条件：公司id、del、分配人（根据userFlag控制是否限制分配人）
     * @param userFlag 是否在查询时限制业绩分配人
     * @param chartResultBasePojo 基础参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/4/5 17:34
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder contractQuery4performance(boolean userFlag, ChartResultBasePojo chartResultBasePojo) throws XbbException {
        BoolQueryBuilder performanceQueryBuilder = boolQueryBuilder4Base(chartResultBasePojo.getCorpid());
        //限制分配人、且非主管或筛选
        if (userFlag && chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            List<String> userIdIn = chartResultBasePojo.getUserIdIn();
            performanceQueryBuilder.filter(termsQuery(ContractPerformanceSubFormEnum.getEsAttr(ContractPerformanceSubFormEnum.USER_ID), userIdIn));
        }
        return performanceQueryBuilder;
    }



    /**
     * 合同业绩分配基础查询条件：公司id、del、分配人（根据userFlag控制是否限制分配人）
     * @param userFlag 是否在查询时限制业绩分配人
     * @param chartResultBasePojo 基础参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/4/5 17:34
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder contractRuleQuery4performance(boolean userFlag, ChartEntity chartEntity, ChartResultBasePojo chartResultBasePojo) throws XbbException {
        BoolQueryBuilder performanceQueryBuilder = boolQueryBuilder4Base(chartResultBasePojo.getCorpid());
        //限制分配人id、且非全公司
        if (userFlag && chartDtoHelp.checkJoinRuleIfAll(chartEntity, chartResultBasePojo)) {
            //es结构内，合同业绩分配为合同的子，限制业绩分配的分配人（筛选范围时，对于不在筛选范围内的员工，但是是业绩分配相关人员，也要放进去计算）
            List<String> userIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            List<Long> contractIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            List<String> pojoUserIdIn = chartResultBasePojo.getUserIdIn();

            Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            params.put(ParameterConstant.CORPID, chartEntity.getCorpid());
            params.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
            params.put("userIdIn", pojoUserIdIn);
            params.put("negRate", 100);
            if(chartResultBasePojo.getStartTime() !=null && chartResultBasePojo.getEndTime() !=null){
                params.put("startTime", chartResultBasePojo.getStartTime());
                params.put("endTime", chartResultBasePojo.getEndTime());
            }
            List<ContractPerformanceEntity> contractPerformanceEntityList = contractPerformanceModel.findEntitys(params);
            contractPerformanceEntityList.forEach(contractPerformanceEntity -> contractIdIn.add(contractPerformanceEntity.getContractId()));
            // 去重
            List<Long> listWithoutContractIn = contractIdIn.stream().distinct().collect(Collectors.toList());
            //params.remove("userIdIn");
            params.put("contractIdIn", listWithoutContractIn);
            List<ContractPerformanceEntity> list = contractPerformanceModel.findEntitys(params);
            list.forEach(contractPerformanceEntity -> userIdIn.add(contractPerformanceEntity.getUserId()));
            // 去重
            List<String> listWithoutUserIdIn = userIdIn.stream().distinct().collect(Collectors.toList());
            pojoUserIdIn.addAll(listWithoutUserIdIn);
            // 去重
            List<String> joinUserIdIn = pojoUserIdIn.stream().distinct().collect(Collectors.toList());
            performanceQueryBuilder.filter(termsQuery(ContractPerformanceSubFormEnum.getEsAttr(ContractPerformanceSubFormEnum.USER_ID), joinUserIdIn));
        }
        return performanceQueryBuilder;
    }

    //-------------------------------跟进记录相关查询条件封装-------------------------------
    /**
     * 所有跟进记录相关统计的共有筛选条件：公司id、del、autoGen、拜访时间
     * @param userFlag 是否在查询时限制创建人list
     * @param chartResultBasePojo 基础参数pojo
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间（区域分布）
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/19 17:00
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder communicateQuery4Base(boolean userFlag, ChartResultBasePojo chartResultBasePojo, boolean limitStart) {
        //时间范围（拜访时间）
        String fieldTime = CustomerCommunicateEnum.getEsAttr4Keyword(CustomerCommunicateEnum.COMMUNICATE_TIME);
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Time(chartResultBasePojo, fieldTime, limitStart);
        //只统计跟进记录类型（当为项目日志时，该值不为0）
        boolQueryBuilder.filter(termQuery("logType", 0));
        //限制创建人id、且非主管或筛选
        if (userFlag && chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            boolQueryBuilder.filter(termsQuery("creatorId.keyword", chartResultBasePojo.getUserIdIn()));
        }
        return boolQueryBuilder;
    }

    /**
     * 所有跟进记录相关统计的共有筛选条件，该方法默认限制开始时间：公司id、del、autoGen、创建人、拜访时间
     * @param chartResultBasePojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/2/27 21:59
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder communicateQuery4UserLimitStart(ChartResultBasePojo chartResultBasePojo) {
        /*--------处理查询条件--------*/
        //默认限制创建人、且限制开始时间
        BoolQueryBuilder boolQueryBuilder = communicateQuery4Base(true, chartResultBasePojo, true);
        return boolQueryBuilder;
    }

    /**
     * 限制业务类型，如跟进机会
     * @param boolQueryBuilder 跟进记录查询条件
     * @param xbbRefTypeEnum 业务类型
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/4/15 20:47
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder communicateFollowQuery(BoolQueryBuilder boolQueryBuilder, XbbRefTypeEnum xbbRefTypeEnum) {
        //限制业务类型
        String communicateBusinessType = CustomerCommunicateEnum.getEsAttr4Keyword(CustomerCommunicateEnum.COMMUNICATE_BUSINESS_TYPE);
        int businessType = xbbRefTypeEnum.getCode();
        boolQueryBuilder.filter(termQuery(communicateBusinessType, businessType));
        return boolQueryBuilder;
    }

    //-------------------------------机会相关查询条件封装-------------------------------
    /**
     * 按照创建人，所有机会相关统计的共有筛选条件：公司id、del、创建人、时间(字段从外部传入)、阶段
     * @param chartResultPojo 参数pojo
     * @param fieldTime 时间字段
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间（如销售机会区域分布）
     * @param saleStageCodeIn 阶段
     * @param isMustNotQuery 是否是取反的阶段code数组  -- 该参数废弃不再使用：阶段推进器需求增加版本、及阶段级联筛选，筛选特定和剔除特定冲突，因此统一改为只统计哪种类型
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/2/27 10:37
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder opportunityQuery4Create(ChartResultPojo chartResultPojo, String fieldTime, boolean limitStart, Collection<String> saleStageCodeIn, boolean isMustNotQuery){
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = opportunityQueryBase(chartResultPojo, fieldTime, limitStart, saleStageCodeIn);
        //创建人id
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            boolQueryBuilder.filter(termsQuery("creatorId.keyword", chartResultPojo.getUserIdIn()));
        }
        if (Objects.nonNull(chartResultPojo.getRefId()) && !Objects.equals(chartResultPojo.getRefId(), 0L) && !Objects.equals(chartResultPojo.getRefId(), -1L)){
            boolQueryBuilder.filter(termQuery("formId", chartResultPojo.getRefId()));
        }
        return boolQueryBuilder;
    }
    /**
     * 新增机会pk查询参数设置
     * @param chartResultPkPojo pk参数
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/21 10:14
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder opportunityQuery4PkAdd(ChartResultPkPojo chartResultPkPojo){
        String fieldTime = "addTime";
        //阶段：0其他普通类型，1赢单，2输单，3取消，不统计取消
        StageFindStrategy stageFindStrategy = StageFindFactory.getStageFindStrategy(XbbRefTypeEnum.SALES_OPPORTUNITY.getCode());
        //允许阶段筛选（阶段筛选器）
        Set<String> statisticsCodeIn = stageFindStrategy.findStageCodeList(chartResultPkPojo.getCorpid(), new StageFindPojo(chartResultPkPojo.getRefId(), StageTypeEnum.getExceptCancelType(), chartResultPkPojo.getArgStrIn()));
        /*--------处理查询条件--------*/
        return opportunityQueryBase(chartResultPkPojo, fieldTime, true, statisticsCodeIn);
    }

    //-------------------------------回款相关查询条件封装-------------------------------
    /**
     * paymentSheet相关统计查询（未开启业绩分配情况，回款、坏账、红冲）共有筛选条件：公司id、del、isBad、回款时间、归属人
     * 查询已回款时：非坏账，不限制红冲  paymentQuery(chartResultPojo, 0, null);
     * 查询坏账时：坏账，不限制红冲  paymentQuery(chartResultPojo, 1, null);
     * 查询红冲时：非坏账，红冲  paymentQuery(chartResultPojo, 0, 1);
     *
     * @param chartResultPojo 参数pojo
     * @param typeList 类型
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/2/28 15:18
     * @version v1.0
     * @since v1.0
     */
    public BoolQueryBuilder paymentQuery4Belong(ChartResultPojo chartResultPojo, List<String> typeList) {
        BoolQueryBuilder boolQueryBuilder = paymentQuery4Base(chartResultPojo, typeList);
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            boolQueryBuilder.filter(termsQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.BELONG_ID) + ".keyword", chartResultPojo.getUserIdIn()));
        }
        List<Long> accountIdList = chartResultPojo.getAccountIdList();
        if (accountIdList != null) {
            boolQueryBuilder.filter(termsQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.FUND_ACCOUNT), accountIdList));
        }
        return boolQueryBuilder;
    }
    /**
     * paymentSheet相关统计查询共有筛选条件：公司id、del、isBad、回款时间
     * 不限制人员，只限制时间，限制特定回款单类型
     *
     * @param chartResultPojo 参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder paymentQuery4Account(ChartResultPojo chartResultPojo) {
        //筛选回款单类型
        List<String> typeList = PaymentSheetTypeEnum.getCodeList4FundAccount();
        BoolQueryBuilder boolQueryBuilder = paymentQuery4Base(chartResultPojo, typeList);
        List<Long> accountIdList = chartResultPojo.getAccountIdList();
        if (accountIdList != null) {
            boolQueryBuilder.filter(termsQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.FUND_ACCOUNT), accountIdList));
        }
        return boolQueryBuilder;
    }


    /**
     * 资金流入统计查询条件封装
     *
     * @param chartResultPojo
     * @return
     */
    public BoolQueryBuilder FundAccountInQuery4Account(ChartResultPojo chartResultPojo) {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, FundAccountFlowEnum.SHEET_TIME.getAttr());
        //处理类型
        boolQueryBuilder.filter(termsQuery(FundAccountFlowEnum.TYPE.getAttr(), FundAccountFlowTypeEnum.getInTypes()));
        List<Long> accountIdList = chartResultPojo.getAccountIdList();
        List<Integer> types = chartResultPojo.getTypes();
        if (accountIdList != null) {
            boolQueryBuilder.filter(termsQuery(FundAccountFlowEnum.ACCOUNT_ID.getAttr(), accountIdList));
        }
        if (CollectionsUtil.isNotEmpty(types)) {
            boolQueryBuilder.filter(termsQuery(FundAccountFlowEnum.TYPE.getAttr(), types));
        }
        return boolQueryBuilder;
    }

    /**
     * 资金流出统计查询条件封装
     *
     * @param chartResultPojo
     * @return
     */
    public BoolQueryBuilder FundAccountOutQuery4Account(ChartResultPojo chartResultPojo) {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, FundAccountFlowEnum.SHEET_TIME.getAttr());
        //处理类型
        boolQueryBuilder.filter(termsQuery(FundAccountFlowEnum.TYPE.getAttr(), FundAccountFlowTypeEnum.getOutTypes()));
        List<Long> accountIdList = chartResultPojo.getAccountIdList();
        List<Integer> types = chartResultPojo.getTypes();
        if (accountIdList != null) {
            boolQueryBuilder.filter(termsQuery(FundAccountFlowEnum.ACCOUNT_ID.getAttr(), accountIdList));
        }
        if (CollectionsUtil.isNotEmpty(types)) {
            boolQueryBuilder.filter(termsQuery(FundAccountFlowEnum.TYPE.getAttr(), types));
        }
        return boolQueryBuilder;
    }



    /**
     * 开启业绩分配情况的回款单（已收款）条件封装
     * 时间通过回款单的回款时间限制、组织架构通过合同的业绩分配进行限制
     * @param userFlag        是否在查询时限制业绩分配人
     * @param chartResultPojo 统计参数pojo
     * @param typeList 类型
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/6 11:18
     * @version v1.0
     * @since v1.0
     */
    public BoolQueryBuilder paymentQuery4Performance(boolean userFlag, ChartResultPojo chartResultPojo, List<String> typeList) throws XbbException {
        //-------------（1）先查回款单，限制回款时间：然后把contractId作为条件塞入performanceQueryBuilder
        BoolQueryBuilder sheetQueryBuilder = paymentQuery4Base(chartResultPojo, typeList);
        BoolQueryBuilder performanceQueryBuilder = contractQuery4performance(userFlag, chartResultPojo);
        ChartFindSheetPojo chartFindSheetPojo = formHelp.findSheet(sheetQueryBuilder, false);
        //-------------（2）查业绩分配，限制组织架构
        List<ContractPerformanceEntity> performanceList = formHelp.getContractPerformanceList(chartResultPojo.getCorpid(), performanceQueryBuilder, chartFindSheetPojo);

        //最终符合条件的合同ids
        Set<Long> contractIdSet = new HashSet<>(performanceList.size());
        contractIdSet.add(-1L);
        for (ContractPerformanceEntity entity : performanceList) {
            contractIdSet.add(entity.getContractId());
        }
        //塞入最终的回款单关联的合同ids（此时得到的合同ids已经是过滤过回款时间、和业绩分配人的结果）
        String contractIdAttr = PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.CONTRACT);
        sheetQueryBuilder.filter(termsQuery(contractIdAttr, contractIdSet));
        List<Long> accountIdList = chartResultPojo.getAccountIdList();
        if (accountIdList != null) {
            sheetQueryBuilder.filter(termsQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.FUND_ACCOUNT), accountIdList));
        }
        return sheetQueryBuilder;
    }

    /**
     * paymentSheet相关统计查询（未开启业绩分配情况，回款、坏账、红冲）共有筛选条件：公司id、del、isBad、回款时间
     * 不限制归属人
     *
     * @param chartResultBasePojo 基础参数pojo
     * @param typeList            类型
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/20 21:01
     * @version v1.0
     * @since v1.0
     */
    public BoolQueryBuilder paymentQuery4Base(ChartResultBasePojo chartResultBasePojo, List<String> typeList) {
        //时间筛选
        String fieldTime = PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.PAYMENT_TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultBasePojo, fieldTime);
        //处理类型
        String typeAttr = PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.PAYMENT_SHEET_TYPE);
        boolQueryBuilder.filter(termsQuery(typeAttr, typeList));
        //不拿子回款单
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), SheetAloneEnum.getNotChildCodeList()));
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 回款单查询条件（合同业绩分配）
     *
     * @param chartResultBasePojo 基础参数pojo
     * @param typeList 类型
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/12/12 9:22
     * @since v1.0
     */
    public BoolQueryBuilder paymentPerformanceQuery(ChartResultBasePojo chartResultBasePojo, List<String> typeList) {
        BoolQueryBuilder paymentSheetQuery = paymentQuery4Base(chartResultBasePojo, typeList);
        esHelper.removeSearchCondition(paymentSheetQuery, FieldTypeEnum.SHEET_ALONE.getAlias());
        paymentSheetQuery.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), SheetAloneEnum.getNotParentCodeList()));
        return paymentSheetQuery;
    }

    /**
     * 未收款查询条件：公司id、del、限制开始和结束时间、未收状态、应收款主负责人
     * @param chartResultBasePojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/4/6 15:05
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder paymentQuery4Un(ChartResultBasePojo chartResultBasePojo) throws XbbException {
        //限制开始结束时间
        String fieldTime = PaymentEnum.getEsAttr4Keyword(PaymentEnum.ESTIMATE_TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Time(chartResultBasePojo, fieldTime, true);
        //只读取未收款状态
        String statusField = PaymentEnum.getEsAttr4Keyword(PaymentEnum.STATUS);
        //未收款状态list：未收款、应收款、部分收款
        List<String> unStatusIn = PaymentStatusEnum.getUnStatusIn();
        boolQueryBuilder.filter(termsQuery(statusField, unStatusIn));

        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            String corpid = chartResultBasePojo.getCorpid();
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_PAYMENT_TEAM, corpid, BasicConstant.MAIN_USER, chartResultBasePojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }
    /**
     * 未完成回款计划查询条件：公司id、del、限制开始和结束时间、未完成状态、新回款计划主负责人
     * @param chartResultBasePojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder paymentTaskQuery4Un(ChartResultBasePojo chartResultBasePojo) {
        //限制开始结束时间
        String fieldTime = PaymentTaskEnum.getEsAttr4Keyword(PaymentTaskEnum.EXECUTOR_TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Time(chartResultBasePojo, fieldTime, true);
        //只读取未完成状态
        String statusField = PaymentTaskEnum.getEsAttr4Keyword(PaymentTaskEnum.STATUS);
        boolQueryBuilder.filter(termQuery(statusField, PaymentTaskStatusEnum.NO_FINISH.getCode()));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            String corpid = chartResultBasePojo.getCorpid();
            //新回款计划无isMain
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_PAYMENT_TASK_TEAM, corpid, null, chartResultBasePojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        return boolQueryBuilder;
    }

    //-------------------------------发票相关查询条件封装-------------------------------
    /**
     * 发票查询条件封装
     * @param chartResultBasePojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/5/6 14:51
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder invoiceQueryBuilder(ChartResultBasePojo chartResultBasePojo){
        String fieldTime = InvoiceEnum.getEsAttr4Keyword(InvoiceEnum.INVOICE_DATE);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultBasePojo, fieldTime);
        String isCancel = InvoiceEnum.getEsAttr4Keyword(InvoiceEnum.IS_CANCEL);
        boolQueryBuilder.filter(termQuery(isCancel, 0));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            //申请人
            boolQueryBuilder.filter(termsQuery(InvoiceEnum.APPLY_USER.getAttr() + ".keyword", chartResultBasePojo.getUserIdIn()));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 进项发票查询条件
     *
     * @param chartResultBasePojo 基础参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2020/3/11 11:29
     * @since v1.0
     */
    public BoolQueryBuilder invoicePurchaseQueryBuilder(ChartResultBasePojo chartResultBasePojo) {
        String fieldTime = PurchaseInvoiceEnum.getEsAttr4Keyword(PurchaseInvoiceEnum.INVOICE_DATE);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultBasePojo, fieldTime);
        String isCancel = PurchaseInvoiceEnum.getEsAttr4Keyword(PurchaseInvoiceEnum.IS_CANCEL);
        boolQueryBuilder.filter(termQuery(isCancel, 0));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            //申请人
            boolQueryBuilder.filter(termsQuery(PurchaseInvoiceEnum.APPLY_USER.getAttr() + ".keyword", chartResultBasePojo.getUserIdIn()));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    //-------------------------------产品相关查询条件封装-------------------------------
    /**
     * 获取合同产品查询条件
     * @param corpid 公司id
     * @param contractIdIn 关联合同id
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/21 21:24
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder productContractQuery(String corpid, Collection<Long> contractIdIn) {
        //此时的contractIdIn只限制了签订时间（非pk榜的还会限制组织架构）
        contractIdIn.add(-1L);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(corpid);
        boolQueryBuilder.filter(termsQuery("contractId", contractIdIn));
        return boolQueryBuilder;
    }

    /**
     * 增加一个关于产品id获取合同产品查询条件
     * @param corpid 公司id
     * @param contractIdIn 关联合同id
     * @param chartResultPkPojo 与图表有关的实体
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author wangtengwei
     * @date 2020/06/18 15:24
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder newProductContractQuery(String corpid, Collection<Long> contractIdIn, ChartResultPkPojo chartResultPkPojo) {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(corpid);
        boolQueryBuilder.filter(termsQuery(ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.CONTRACT_ID), contractIdIn));
        if (chartResultPkPojo.getPerformancePk()) {
            PerformanceSetExplainPojo explainPojo = performanceHelp.getPerformanceSetExplainPojo(chartResultPkPojo.getChartEntity());
            List<Long> assessProductList = explainPojo.getAccessProductIds();
            //如果业绩目标，则过滤设置过的考核产品
            if (assessProductList != null && assessProductList.size() > 0) {
                boolQueryBuilder.filter(termsQuery(ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.PRODUCT_ID), assessProductList));
            }
        }
        return boolQueryBuilder;
    }

    /**
     * 处理产品销量底层查询条件
     * @param chartResultBasePojo 基础查询参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/4/2 14:25
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder productSalesQuery(ChartResultBasePojo chartResultBasePojo) throws XbbException {
        String corpid = chartResultBasePojo.getCorpid();
        //塞入合同查询条件：是否限制签订人根据signUserFlag、不限制红冲
        BoolQueryBuilder contractQueryBuilder = contractQuery4LimitStart(true, null, chartResultBasePojo);
        /*--------此时查出的合同限制了签订时间、签订人--------*/
        //筛选签订时间 --筛选合同id（因为关联表无时间、无userId，故筛选时间和筛选部门(员工)只能根据合同）
        Set<Long> contractIdIn = formHelp.getDataIdList(corpid, contractQueryBuilder, IndexTypeEnum.IDX_SAAS_CONTRACT);
        /*--------根据限制了签订时间、签订人的合同ids查询合同产品--------*/
        BoolQueryBuilder productQueryBuilder = productContractQuery(corpid, contractIdIn);
        return productQueryBuilder;
    }
    /**
     * 产品成本的查询参数
     *
     * @param chartResultPojo corpid
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/3/31 10:49
     * @since v1.0
     */
    public BoolQueryBuilder productCostQuery(ChartResultPojo chartResultPojo) {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(chartResultPojo.getCorpid());
        String parentId = ProductEnum.getEsAttr4Keyword(ProductEnum.PARENT_ID);
        //父产品id不为0
        boolQueryBuilder.mustNot(termQuery(parentId, BasicConstant.ZERO));
        return boolQueryBuilder;
    }

    /**
     * 单个产品销量筛选条件
     * @param chartResultBasePojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder productSalesSingleQuery(ChartResultBasePojo chartResultBasePojo) {
        String corpid = chartResultBasePojo.getCorpid();
        BoolQueryBuilder productQueryBuilder = boolQueryBuilder4Base(corpid);
        //获取合同产品参数
        Long productId = chartResultBasePojo.getRefId();
        //筛选单个产品
        if (productId != null && productId > 0) {
            productQueryBuilder.filter(termQuery(ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.PRODUCT_ID), productId));
        }
        return productQueryBuilder;
    }

    /**
     * 单个产品销量的筛选条件（以父<合同>为主）
     *
     * @param userFlag 是否在查询时限制签订人
     * @param isRed 是否红冲，null不作为筛选条件，1是0否
     * @param chartResultBasePojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException 处理合同参数抛出异常
     * @author 徐俊杰
     * @date 2019/4/25 15:05
     * @since v1.0
     */
    public BoolQueryBuilder productSalesSingleQuery4Parent(boolean userFlag, Integer isRed, ChartResultBasePojo chartResultBasePojo) throws XbbException {
        //处理合同参数
        BoolQueryBuilder boolQueryBuilder = contractQuery4LimitStart(userFlag, isRed, chartResultBasePojo);
        esHelper.removeSearchCondition(boolQueryBuilder, "formId");
        //获取合同产品参数
        Long productId = chartResultBasePojo.getRefId();
        if (productId != null && productId > 0) {
            //传入合同产品参数
            BoolQueryBuilder productQueryBuilder = boolQueryBuilder4Base(chartResultBasePojo.getCorpid());
            productQueryBuilder.filter(termQuery(ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.PRODUCT_ID), productId));
            HasChildQueryBuilder hasChildQueryBuilder = JoinQueryBuilders.hasChildQuery(IndexTypeEnum.IDX_SAAS_CONTRACT_PRODUCT.getType(), productQueryBuilder, ScoreMode.Max);
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        if (chartResultBasePojo.getPerformancePk()) {
            PerformanceSetExplainPojo explainPojo = performanceHelp.getPerformanceSetExplainPojo(chartResultBasePojo.getChartEntity());
            List<Long> assessProductList = explainPojo.getAccessProductIds();
            //如果业绩目标，则过滤设置过的考核产品
            if (assessProductList != null && assessProductList.size() > 0) {
                //传入合同产品参数
                BoolQueryBuilder productQueryBuilder = boolQueryBuilder4Base(chartResultBasePojo.getCorpid());
                productQueryBuilder.filter(termsQuery(ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.PRODUCT_ID), assessProductList));
                HasChildQueryBuilder hasChildQueryBuilder = JoinQueryBuilders.hasChildQuery(IndexTypeEnum.IDX_SAAS_CONTRACT_PRODUCT.getType(), productQueryBuilder, ScoreMode.Max);
                boolQueryBuilder.filter(hasChildQueryBuilder);
            }
        }
        return boolQueryBuilder;
    }
    /**
     * 单个产品销量的筛选条件（以子<合同产品为主>为主）
     *
     * @param chartResultPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/6 15:22
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder productSalesSingleQuery4Child(ChartResultPojo chartResultPojo, Integer isRed, Long productId) throws XbbException {
        String corpid = chartResultPojo.getCorpid();
        //传入合同产品参数
        BoolQueryBuilder productQueryBuilder = boolQueryBuilder4Base(corpid);
        //筛选单个产品
        if (productId != null && productId > 0) {
            productQueryBuilder.filter(termQuery(ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.PRODUCT_ID), productId));
        }
        /**
         此处传入的refId是单个产品的id，所以基础合同条件内的formId限制用refId是不对的，要移除
         把合同相关作为父级条件
         */
        BoolQueryBuilder contractQueryBuilder = contractQuery4Sign(isRed, chartResultPojo);
        esHelper.removeSearchCondition(contractQueryBuilder, "formId");
        HasParentQueryBuilder hasParentQueryBuilder = JoinQueryBuilders.hasParentQuery(IndexTypeEnum.IDX_SAAS_CONTRACT.getType(), contractQueryBuilder, false);
        productQueryBuilder.filter(hasParentQueryBuilder);
        return productQueryBuilder;
    }

    /**
     * 订单关联的产品
     *
     * @param chartResultPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author weiming
     * @date 2020/1/16 15:22
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder orderProduct4Distributor(ChartResultPojo chartResultPojo, Integer isRed, Long productId) throws XbbException {
        String corpid = chartResultPojo.getCorpid();
        //传入合同产品参数
        BoolQueryBuilder productQueryBuilder = boolQueryBuilder4Base(corpid);
        productQueryBuilder.filter(termQuery(ContractProductSubFormEnum.getEsAttr(ContractProductSubFormEnum.TYPE), XbbRefTypeEnum.CONTRACT.getCode()));
        String fieldTime = "addTime";
        BoolQueryBuilder contractQueryBuilder = orderQuery4Team(chartResultPojo, fieldTime, true);
        //查询formId,封装formIdList
        List<Long> formIdList = paasFormModel.getFormIdList4Distributor(
                XbbRefTypeEnum.CONTRACT.getCode(), chartResultPojo.getCorpid(), 1);
        contractQueryBuilder.filter(termsQuery("formId", formIdList));
        contractQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
        //订单状态：待发货、待收货、已完成、已关闭。
        contractQueryBuilder.filter(termsQuery(OrderEnum.getAttrConnectData(OrderEnum.STATUS), OrderStatusEnum.getCode4Statistics()));
        List<JSONObject> searchList = chartResultPojo.getSearch();
        if (CollectionsUtil.isNotEmpty(searchList)) {
            for (int i = 0; i < searchList.size(); i++) {
                JSONObject jsonObject = searchList.get(i);
                ConditionsEntityExt condition = JSON.parseObject(jsonObject.toJSONString(), ConditionsEntityExt.class);
                condition.setAttr("data."+condition.getAttr());
                ConditionEnum conditionEnum = ConditionEnum.getConditionEnum(condition.getSymbol());
                EsUtil.commonConditionSymbolChoose(contractQueryBuilder, conditionEnum, condition);
            }
        }
        HasParentQueryBuilder hasParentQueryBuilder = JoinQueryBuilders.hasParentQuery(IndexTypeEnum.IDX_SAAS_CONTRACT.getType(), contractQueryBuilder, false);
        productQueryBuilder.filter(hasParentQueryBuilder);
        return productQueryBuilder;
    }

    /**
     *退货单关联的产品
     *
     * @param chartResultPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author weiming
     * @date 2020/1/16 15:22
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder returnProduct4Distributor(ChartResultPojo chartResultPojo, Integer isRed, Long productId) throws XbbException {
        String corpid = chartResultPojo.getCorpid();
        //传入合同产品参数
        BoolQueryBuilder productQueryBuilder = boolQueryBuilder4Base(corpid);
        productQueryBuilder.filter(termQuery(ContractProductSubFormEnum.getEsAttr(ContractProductSubFormEnum.TYPE), XbbRefTypeEnum.REFUND.getCode()));
        /**
         此处传入的refId是单个产品的id，所以基础合同条件内的formId限制用refId是不对的，要移除
         把合同相关作为父级条件
         */
        BoolQueryBuilder contractQueryBuilder = orderQuery4Team(chartResultPojo, "addTime", true);
        //封装formIdList查询条件
        List<Long> orderFormIdList = paasFormModel.getFormIdList4Distributor(
                XbbRefTypeEnum.CONTRACT.getCode(), chartResultPojo.getCorpid(), 1);
        contractQueryBuilder.filter(termsQuery("formId",orderFormIdList));
        contractQueryBuilder.filter(termQuery(ContractEnum.getAttrConnectData(ContractEnum.IS_RED), 1));
        contractQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType())));
        List<JSONObject> searchList = chartResultPojo.getSearch();
        if (CollectionsUtil.isNotEmpty(searchList)) {
            for (int i = 0; i < searchList.size(); i++) {
                JSONObject jsonObject = searchList.get(i);
                ConditionsEntityExt condition = JSON.parseObject(jsonObject.toJSONString(), ConditionsEntityExt.class);
                condition.setAttr("data."+condition.getAttr());
                ConditionEnum conditionEnum = ConditionEnum.getConditionEnum(condition.getSymbol());
                EsUtil.commonConditionSymbolChoose(contractQueryBuilder, conditionEnum, condition);
            }
        }
        HasParentQueryBuilder hasParentQueryBuilder = JoinQueryBuilders.hasParentQuery(IndexTypeEnum.IDX_SAAS_CONTRACT.getType(), contractQueryBuilder, false);
        productQueryBuilder.filter(hasParentQueryBuilder);
        return productQueryBuilder;
    }

    /**
     * 合同产品查询参数（父查子）
     *
     * @param chartResultPojo 入参
     * @param isRed 是否是红冲合同
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException 处理合同参数抛出异常
     * @author 徐俊杰
     * @date 2019/4/28 9:15
     * @since v1.0
     */
    public BoolQueryBuilder contractProductQuery(ChartResultPojo chartResultPojo, Integer isRed) throws XbbException {
        //处理合同产品参数
        String nameOrNoLike = chartResultPojo.getNameLike();
        BoolQueryBuilder contractProductQuery = productSalesSingleQuery4Child(chartResultPojo, isRed, null);
        if (!StringUtil.isEmpty(nameOrNoLike)) {
            contractProductQuery.should(matchPhraseQuery(ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.PRODUCT_NAME), nameOrNoLike));
            contractProductQuery.should(matchPhraseQuery(ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.PRODUCT_NO), nameOrNoLike));
            contractProductQuery.minimumShouldMatch(1);
        }
        return contractProductQuery;
    }

    //-------------------------------退货（红冲）合同相关查询条件封装-------------------------------
    /**
     * 退货合同（红冲合同）查询条件封装
     * @param openRule 是否开启业绩分配，统计数量时传false即可
     * @param chartResultPojo 统计参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/6 19:32
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder refundQueryBuilder(boolean openRule, ChartResultPojo chartResultPojo) throws XbbException {
        String corpid = chartResultPojo.getCorpid();
        //先查红冲合同ids
        BoolQueryBuilder redQueryBuilder;
        if (openRule) {
            //符合条件的合同查询条件：签订时间、分配人
            redQueryBuilder = contractQuery4performance2Parent(1, true, chartResultPojo);
        } else {
            redQueryBuilder = contractQuery4Sign(1, chartResultPojo);
        }
        Set<Long> redContractIdIn = formHelp.getDataIdList(corpid, redQueryBuilder, IndexTypeEnum.IDX_SAAS_CONTRACT);
        redContractIdIn.add(-1L);
        String contractIdAttr = RefundEnum.getEsAttr4Keyword(RefundEnum.RED_CONTRACT_ID);
        //然后拿红冲合同id去查询退货表（条件塞入退货查询）
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(corpid);
        boolQueryBuilder.filter(termsQuery(contractIdAttr, redContractIdIn));
        return boolQueryBuilder;
    }

    //-------------------------------生产单相关查询条件封装-------------------------------
    /**
     * 生产单【按照负责人】相关统计的共有筛选条件：corpid, userIdIn, addTime
     * @param chartResultPojo 统计参数pojo
     * @param fieldTime 统计时间字段
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/4/10 19:03
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder productionOrderQuery4Team(ChartResultPojo chartResultPojo, String fieldTime) {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, fieldTime);
        //生产单负责人处理
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            String corpid = chartResultPojo.getCorpid();
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_PRODUCTION_ORDER_TEAM, corpid, BasicConstant.MAIN_USER, chartResultPojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 已完成生产单相关统计的公有筛选条件：corpid, dataId, del, data.status, time
     *
     * @param chartResultPojo corpid, userIdIn, startTime, endTime
     * @param script          字段间比较的条件
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/3/26 9:07
     * @since v1.0
     */
    public BoolQueryBuilder productionOrderQuery4Complete(ChartResultPojo chartResultPojo, Script script) {
        String fieldTime = ProductionOrderEnum.getEsAttr4Keyword(ProductionOrderEnum.PLAN_TIME);
        BoolQueryBuilder boolQueryBuilder = productionOrderQuery4Team(chartResultPojo,fieldTime);
        //状态已完成
        String status = ProductionOrderEnum.getEsAttr4Keyword(ProductionOrderEnum.STATUS);
        boolQueryBuilder.filter(termQuery(status, ProductionOrderStatusEnum.FINISHED.getCode()));
        //可不传script
        if (null != script) {
            boolQueryBuilder.filter(scriptQuery(script));
        }
        return boolQueryBuilder;
    }

    //-------------------------------采购合同、采购退货相关查询条件封装-------------------------------
    /**
     * 采购合同相关统计的公有筛选条件：corpid、del、signTime、signerId
     *
     * @param chartResultPojo 内部存系统指标统计时需要的参数
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/3/13 11:28
     * @since v1.0
     */
    public BoolQueryBuilder purchaseQuery(ChartResultPojo chartResultPojo) {
        String fieldTime = PurchaseEnum.getEsAttr4Keyword(PurchaseEnum.SIGN_TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, fieldTime);
        //员工筛选
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            boolQueryBuilder.filter(termsQuery(PurchaseEnum.getEsAttr4Keyword(PurchaseEnum.SIGNER_ID), chartResultPojo.getUserIdIn()));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 采购退货相关统计的公有筛选条件：corpid、del、time、managerId
     * @param chartResultPojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/4/9 22:17
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder returnedPurchaseQuery(ChartResultPojo chartResultPojo) {
        //退货日期
        String fieldTime = ReturnedPurchaseEnum.getEsAttr4Keyword(ReturnedPurchaseEnum.TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, fieldTime);
        //退货负责人
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            boolQueryBuilder.filter(termsQuery(ReturnedPurchaseEnum.MANAGER_ID.getAttr() + ".keyword", chartResultPojo.getUserIdIn()));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    //-------------------------------付款相关查询条件封装-------------------------------
    /**
     * 未付款相关统计的公有筛选条件：corpid、del、estimateTime
     *
     * @param chartResultPojo 内部存系统指标统计时需要的参数
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/3/13 15:20
     * @since v1.0
     */
    public BoolQueryBuilder payQuery4Un(ChartResultPojo chartResultPojo) {
        String fieldTime = PayPlanEnum.getEsAttr4Keyword(PayPlanEnum.ESTIMATE_TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Time(chartResultPojo, fieldTime, true);
        //只读取未付款、部分付款状态
        String statusField = PayPlanEnum.getEsAttr4Keyword(PayPlanEnum.STATUS);
        //未收款状态list：未收款、应收款、部分收款
        List<String> unStatusIn = PayPlanStatusEnum.getUnStatusIn();
        boolQueryBuilder.filter(termsQuery(statusField, unStatusIn));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_PAY_PLAN_TEAM, chartResultPojo.getCorpid(), BasicConstant.MAIN_USER, chartResultPojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }
    /**
     * 付款单相关统计的公有筛选条件：corpid、del、realPayTime
     *
     * @param chartResultPojo 内部存系统指标统计时需要的参数
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/3/18 15:54
     * @since v1.0
     */
    public BoolQueryBuilder paySheetQuery(ChartResultPojo chartResultPojo, List<String> typeList) {
        String fieldTime = PaySheetEnum.getEsAttr4Keyword(PaySheetEnum.REAL_PAY_TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, fieldTime);
        String typeAttr = PaySheetEnum.getEsAttr4Keyword(PaySheetEnum.PAY_SHEET_TYPE);
        boolQueryBuilder.filter(termsQuery(typeAttr, typeList));
        //不拿子付款单
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), SheetAloneEnum.getNotChildCodeList()));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            String owner = PaySheetEnum.getEsAttr4Keyword(PaySheetEnum.BELONG_ID);
            boolQueryBuilder.filter(termsQuery(owner, chartResultPojo.getUserIdIn()));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        List<Long> accountIdList = chartResultPojo.getAccountIdList();
        if (accountIdList != null) {
            boolQueryBuilder.filter(termsQuery(PaySheetEnum.getEsAttr4Keyword(PaySheetEnum.FUND_ACCOUNT), accountIdList));
        }
        return boolQueryBuilder;
    }
    /**
     * paySheet相关统计查询共有筛选条件：公司id、del、isBad、付款时间
     * 不限制人员，只限制时间，限制特定付款单类型
     * @param chartResultPojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder paySheetQuery4Account(ChartResultPojo chartResultPojo) {
        List<String> typeList = PaySheetTypeEnum.getCodeList4FundAccount();
        String fieldTime = PaySheetEnum.getEsAttr4Keyword(PaySheetEnum.REAL_PAY_TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, fieldTime);
        //不拿子付款单
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), SheetAloneEnum.getNotChildCodeList()));
        String typeAttr = PaySheetEnum.getEsAttr4Keyword(PaySheetEnum.PAY_SHEET_TYPE);
        boolQueryBuilder.filter(termsQuery(typeAttr, typeList));
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        List<Long> accountIdList = chartResultPojo.getAccountIdList();
        if (accountIdList != null) {
            boolQueryBuilder.filter(termsQuery(PaySheetEnum.getEsAttr4Keyword(PaySheetEnum.FUND_ACCOUNT), accountIdList));
        }
        return boolQueryBuilder;
    }

    //-------------------------------入库相关查询条件封装-------------------------------
    /**
     * 限制入库时间、限制负责人
     * @param chartResultPojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/4/8 15:33
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder instockQuery4Manages(ChartResultPojo chartResultPojo) {
        //限制入库时间
        String fieldTime = InstockEnum.getEsAttr4Keyword(InstockEnum.TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, fieldTime);
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            String managerId = InstockEnum.MANAGER_ID.getAttr() + ".keyword";
            boolQueryBuilder.filter(termsQuery(managerId, chartResultPojo.getUserIdIn()));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }
    /**
     * 入库单查询参数，某些关联单据的入库单查询条件，可根据typeIn查询不同的入库类型单据
     *
     * @param chartResultPojo 基础参数
     * @param refIdSet        关联单据id集合
     * @param typeIn          入库类型合集
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/3/30 13:39
     * @since v1.0
     */
    public BoolQueryBuilder instockQueryByIds4Type(ChartResultPojo chartResultPojo, Set<Long> refIdSet, List<Integer> typeIn) {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(chartResultPojo.getCorpid());
        //入库类型-生产成品入库
        String type = InstockEnum.getEsAttr4Keyword(InstockEnum.TYPE);
        boolQueryBuilder.filter(termsQuery(type, typeIn));
        //关联单据
        String refId = InstockEnum.getEsAttr4Keyword(InstockEnum.REF_ID);
        boolQueryBuilder.filter(termsQuery(refId, refIdSet));
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    //-------------------------------出库相关查询条件封装-------------------------------
    /**
     * 限制出库时间、限制负责人
     * @param chartResultPojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/4/8 20:48
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder outstockQuery4Manages(ChartResultPojo chartResultPojo) {
        //限制入库时间，限制负责人，不限制出库类型
        return outstockQuery4Types(chartResultPojo, null);
    }

    /**
     * 限制出库时间、限制负责人，根据typeIn是否为null来决定是否限制出库类型
     * @param chartResultPojo 参数pojo
     * @param typeIn 出库类型枚举 @see OutstockTypeEnum
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/4/9 16:02
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder outstockQuery4Types(ChartResultPojo chartResultPojo, List<Integer> typeIn) {
        //限制入库时间
        String fieldTime = OutstockEnum.getEsAttr4Keyword(OutstockEnum.TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, fieldTime);
        if (typeIn != null) {
            boolQueryBuilder.filter(termsQuery(OutstockEnum.getEsAttr4Keyword(OutstockEnum.TYPE), typeIn));
        }
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            String managerId = OutstockEnum.MANAGER_ID.getAttr() + ".keyword";
            boolQueryBuilder.filter(termsQuery(managerId, chartResultPojo.getUserIdIn()));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 出库单参数（合同id筛选）
     *
     * @param corpid 公司id
     * @param contractIdSet 合同id集合
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/4/28 15:08
     * @since v1.0
     */
    public BoolQueryBuilder outstockQuery4ContractId(String corpid, Set<Long> contractIdSet) {
        //出库类型
        String type = OutstockEnum.getEsAttr4Keyword(OutstockEnum.TYPE);
        //关联单据
        String refId = OutstockEnum.getEsAttr4Keyword(OutstockEnum.REF_ID);
        BoolQueryBuilder outstockQuery = boolQueryBuilder4Base(corpid);
        outstockQuery.filter(termQuery(type, OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode()));
        outstockQuery.filter(termsQuery(refId, contractIdSet));
        //无需审批和通过
        outstockQuery.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return outstockQuery;
    }

    //-------------------------------工单相关查询条件封装-------------------------------

    /**
     * 工单查询条件封装基本方法，默认限制开始时间
     *
     * @param chartResultPojo corpid, del, creatorIdIn, startTime, endTime, templateId
     * @param fieldTime       要进行时间筛选的字段
     * @param status          流程状态@see WorkOrderStatusEnum
     * @param script          字段间比较的条件
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/4/12 16:27
     * @since v1.0
     */
    public BoolQueryBuilder workOrderQuery4Base(ChartResultPojo chartResultPojo, String fieldTime, Integer status, Script script) {
        //默认限制开始时间
        return workOrderQuery4LimitStart(chartResultPojo, fieldTime, true, status, script);
    }

    /**
     * 工单查询条件封装，工单耗时分析中的自由指派工单使用
     *
     * @param chartResultPojo corpid, del, creatorIdIn, startTime, endTime, templateId
     * @param fieldTime 要进行时间筛选的字段
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/6/11 13:47
     * @since v1.0
     */
    BoolQueryBuilder workOrderQuery4FinishTime(ChartResultPojo chartResultPojo, String fieldTime) {
        BoolQueryBuilder workOrderQuery = workOrderQuery4LimitStart(chartResultPojo, fieldTime, true, WorkOrderStatusEnum.FINISH.getCode(), null);
        String expectedTimeAttr = WorkOrderForStatisticsEnum.EXPECTED_TIME.getSaasAttr();
        //排除预计时间为0的情况，也就是不限时的情况
        int startTime = TimeConstant.ZERO;
        workOrderQuery.filter(rangeQuery(expectedTimeAttr).gt(startTime));
        //默认限制开始时间
        return workOrderQuery;
    }

    /**
     * 工单查询条件封装
     *
     * @param chartResultPojo 参数pojo
     * @param fieldTime       要进行时间筛选的字段
     * @param limitStart      是否限制开始时间，true限制开始时间，false不限制开始时间
     * @param status          流程状态@see WorkOrderStatusEnum
     * @param script          字段间比较的条件
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/7 17:34
     * @since v1.0
     */
    public BoolQueryBuilder workOrderQuery4LimitStart(ChartResultPojo chartResultPojo, String fieldTime, boolean limitStart, Integer status, Script script) {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Time(chartResultPojo, fieldTime, limitStart);
        Long templateId = chartResultPojo.getRefId();
        /*--------处理查询条件--------*/
        if (templateId != null && templateId > 0) {
            boolQueryBuilder.filter(termQuery("templateId", templateId));
        }
        //限制创建人id、且非主管或筛选
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            boolQueryBuilder.filter(termsQuery("creatorId.keyword", chartResultPojo.getUserIdIn()));
        }
        //流程状态筛选
        if (null != status) {
            boolQueryBuilder.filter(termQuery("status", status));
        }
        if (null != script) {
            boolQueryBuilder.filter(scriptQuery(script));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    //-------------------------------呼叫中心相关-------------------------------
    /**
     * 呼出时长相关统计的共有筛选条件：corpid、del、type、userId、addTime
     * 封装呼叫坐席数需要的关键条件：corpid、del、type、userId、addTime
     *
     * @param chartResultPojo
     * @param outOrIn 呼出1：true ;呼入0：false
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author youli.chen
     * @date 2019/3/1 15:30
     * @version v1.0
     * @since v1.0
     * @update 2019-4-17 从CrmCallLogResult移入，by zcp
     */
    public BoolQueryBuilder callLogQuery(ChartResultPojo chartResultPojo, boolean outOrIn) throws XbbException {
        String fieldTime = "addTime";
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, fieldTime);
        // 呼出：1 ;呼入：0
        if (outOrIn) {
            boolQueryBuilder.filter(rangeQuery("type").gte(BasicConstant.ONE).lte(BasicConstant.ONE));
        } else {
            boolQueryBuilder.filter(rangeQuery("type").gte(BasicConstant.ZERO).lte(BasicConstant.ZERO));
        }
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            boolQueryBuilder.filter(termsQuery("userId.keyword", chartResultPojo.getUserIdIn()));
        }
        Integer code = CallCenterCallLogCallSourceEnum.CALL_CENTER.getCode();
        boolQueryBuilder.filter(rangeQuery("callSource").gte(code).lte(code));
        return boolQueryBuilder;
    }


    //-------------------------------资金账户相关-------------------------------
    /**
     * 资金账户收支情况，流水表查询条件
     * @param chartResultPojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder fundAccountFlowQuery(ChartResultPojo chartResultPojo) {
        //单据时间
        String fieldTime = FundAccountFlowEnum.SHEET_TIME.getAttr();
        //corpid,del,time
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, fieldTime);
        //账户列表
        List<Long> accountIdList = chartResultPojo.getAccountIdList();
        //类型
        List<Integer> types = chartResultPojo.getTypes();
        if (accountIdList != null) {
            boolQueryBuilder.filter(termsQuery(FundAccountFlowEnum.ACCOUNT_ID.getAttr(), accountIdList));
        }
        if (CollectionsUtil.isNotEmpty(types)) {
            boolQueryBuilder.filter(termsQuery(FundAccountFlowEnum.TYPE.getAttr(), types));
        }
        return boolQueryBuilder;
    }

    /**
     * 获取当页之前所有数据的汇总
     * @param chartResultPojo
     * @param firstSheetTime 某页第一条流水的sheetTime
     * @param firstFlowId 某页第一条流水的flowId
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder fundAccountFlowQuery4PageFirstBefore(ChartResultPojo chartResultPojo, Long firstSheetTime, Long firstFlowId) {
        //单据时间
        //corpid,del,accountId，flowId小于
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", chartResultPojo.getCorpid()));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        //账户列表
        List<Long> accountIdList = chartResultPojo.getAccountIdList();
        if (accountIdList != null) {
            boolQueryBuilder.filter(termsQuery("accountId", accountIdList));
        }
        /*
         sheetTime小于firstSheetTime
         or
         sheetTime与firstSheetTime相等，且流水id小于该页第一个firstFlowId
         */
        BoolQueryBuilder oneQueryBuilder = boolQuery();
        BoolQueryBuilder twoQueryBuilder = boolQuery();
        oneQueryBuilder.filter(rangeQuery("sheetTime").lt( firstSheetTime));
        twoQueryBuilder.filter(termQuery("sheetTime", firstSheetTime));
        twoQueryBuilder.filter(rangeQuery(BusinessConstant.ID).lt( firstFlowId));
        boolQueryBuilder.should(oneQueryBuilder);
        boolQueryBuilder.should(twoQueryBuilder);
        boolQueryBuilder.minimumShouldMatch(MINI_MUM_SHOULD_MATCH);
        //增加一个单据类型的搜索条件
        if (CollectionsUtil.isNotEmpty(chartResultPojo.getTypes())) {
            boolQueryBuilder.filter(termsQuery(FundAccountFlowEnum.TYPE.getAttr(), chartResultPojo.getTypes()));
        }
        return boolQueryBuilder;
    }

    /**
     * 全景图查询条件：必须限制业务id，获取当前登录人及其下属的数据，且不限制时间
     * @param panoramaPojo 全景图参数pojo
     * @param refId 关联业务id
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/6/3 16:49
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder boolQueryBuilder4PanoramaCall(PanoramaPojo panoramaPojo, Long refId) {
        /*--------处理查询条件--------*/
        //限制基础条件：corpid、del
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(panoramaPojo.getCorpid());
        //限制业务id
        boolQueryBuilder.filter(termQuery("refType", XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode()));
        boolQueryBuilder.filter(termQuery("refId", refId));
        //限制人（获取到自己及其下属的数据）
        if (chartDtoHelp.judgePermissionAndIfCheck(panoramaPojo)) {
            boolQueryBuilder.filter(termsQuery("userId.keyword", panoramaPojo.getUserIdIn()));
        }
        return boolQueryBuilder;
    }

    /**
     * 呼叫统计（单个员工）
     *
     * @param callReckonPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/6/26 16:26
     * @since v1.0
     */
    public BoolQueryBuilder callReckonQuery(CallReckonPojo callReckonPojo) {
        //限制基础条件：corpid、del
        BoolQueryBuilder callQuery = boolQueryBuilder4Base(callReckonPojo.getCorpid());
        //时间
        Integer startTime = callReckonPojo.getStartTime();
        Integer endTime = callReckonPojo.getEndTime();
        String timeAttr = callReckonPojo.getTimeAttr();
        callQuery.filter(rangeQuery(timeAttr).gte(startTime).lt(endTime));
        //用户
        String userId = callReckonPojo.getUserId();
        callQuery.filter(termsQuery("userId.keyword", userId));
        //通话类型
        Integer type = callReckonPojo.getType();
        callQuery.filter(rangeQuery("type").gte(type).lte(type));
        // 只统计销帮帮呼叫中心
        Integer code = CallCenterCallLogCallSourceEnum.CALL_CENTER.getCode();
        callQuery.filter(rangeQuery("callSource").gte(code).lte(code));
        return callQuery;
    }

    /**
     * 呼叫统计（单个员工）(筛选是否成功)
     *
     * @param callReckonPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/6/27 10:14
     * @since v1.0
     */
    public BoolQueryBuilder callSuccessQuery(CallReckonPojo callReckonPojo) {
        BoolQueryBuilder callQuery = callReckonQuery(callReckonPojo);
        Integer result = callReckonPojo.getResult();
        callQuery.filter(rangeQuery("result").gte(result).lte(result));
        return callQuery;
    }

    //-------------------------------报价单相关查询条件封装-------------------------------
    /**
     * 报价单数量参数
     *
     * @param basePojo 图表基础参数
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/8/15 16:48
     * @since v1.0
     */
    public BoolQueryBuilder quotationCountQuery(ChartResultBasePojo basePojo) {
        return quotationQuery4Base(basePojo);
    }
    /**
     * 报价单成交数量参数
     *
     * @param basePojo 图表基础参数
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/8/22 17:25
     * @since v1.0
     */
    public BoolQueryBuilder quotationCreateContractCountQuery(ChartResultBasePojo basePojo) {
        BoolQueryBuilder quotationQuery = quotationQuery4Base(basePojo);
        quotationQuery.filter(termQuery(QuotationEnum.getEsAttr4Keyword(QuotationEnum.STATUS), QuotationStatusEnum.CREATED.getCode()));
        return quotationQuery;
    }

    //-------------------------------穿透方法-------------------------------
    /**
     * 客户指标穿透查询条件封装
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/1 17:02
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder throughQuery4Customer(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String corpid = resultThroughPojo.getCorpid();
        Long formId = resultThroughPojo.getRefId();
        String dictionaryCode = resultThroughPojo.getDictionaryCode();
        String head = resultThroughPojo.getHead();
        String businessTitle = "";
        BoolQueryBuilder boolQueryBuilder;
        String fieldTime = "addTime";
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        switch (indicatorRuleEnum) {
            case CUSTOMER_ADD:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = customerQuery4Create(true, resultThroughPojo, fieldTime);
                break;
            case CUSTOMER_LOSE_REASON:
                if (formId == null || formId <= 0) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), I18nMessageUtil.getMessage(CommonConstant.FORM));
                }
                if (StringUtil.isEmpty(dictionaryCode)) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), I18nMessageUtil.getMessage(I18nStringConstant.CUSTOMER_STAGE));
                }
                DictionaryEnum reasonEnum = DictionaryEnum.getEnumByIndicatorRuleEnum(indicatorRuleEnum);
                //查询出字典list
                List<ItemPoJo> reasonList = dataDictionaryModel.getDictionaryList4Saas(reasonEnum, corpid, formId);
                String reasonCodeTitle = commonHelp.formatThroughDictionary(dictionaryCode, indicatorRuleEnum.getName(), reasonList);
                businessTitle = indicatorRuleEnum.getThroughName() + reasonCodeTitle;

                boolQueryBuilder = customerQueryBaseForLose(resultThroughPojo, true, dictionaryCode, true);
                break;
            case CUSTOMER_BEFORE_STAGE:
                if (formId == null || formId <= 0) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), I18nMessageUtil.getMessage(CommonConstant.FORM));
                }
                if (StringUtil.isEmpty(dictionaryCode)) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), I18nMessageUtil.getMessage(I18nStringConstant.CUSTOMER_STAGE));
                }

                boolQueryBuilder = customerQueryBaseForLose(resultThroughPojo, true, dictionaryCode, false);

                List<ItemPoJo> stageList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                List<CustomerStageEntity> list = customerStageModel.getList(corpid,formId, null);
                for(CustomerStageEntity stage : list){
                    String stageCode = String.valueOf(stage.getCode());
                    if(Objects.equals(stage.getType(), StageTypeEnum.FAIL_STAGE.getType())){
                        continue;
                    }
                    ItemPoJo itemPoJo = new ItemPoJo();
                    itemPoJo.setText(stage.getName());
                    itemPoJo.setValue(stageCode);
                    stageList.add(itemPoJo);
                }

                String stageTitle = commonHelp.formatThroughDictionary(dictionaryCode, indicatorRuleEnum.getName(), stageList);
                businessTitle = indicatorRuleEnum.getThroughName() + stageTitle;
                break;
            case CUSTOMER_FUNNEL:
                if (formId == null || formId <= 0) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), I18nMessageUtil.getMessage(CommonConstant.FORM));
                }
                if (StringUtil.isEmpty(dictionaryCode)) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), I18nMessageUtil.getMessage(I18nStringConstant.CUSTOMER_STAGE));
                }
                businessTitle = indicatorRuleEnum.getThroughName();
                //boolQueryBuilder = customerQuery4Team(resultThroughPojo, fieldTime, true);
                //boolQueryBuilder = boolQueryBuilder4Form(resultThroughPojo, fieldTime, true);
                /*--------处理查询条件--------*/
                boolQueryBuilder = boolQueryBuilder4Base(corpid);
                boolQueryBuilder.filter(termQuery("formId", formId));
                //无需审批和通过
                boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
                customerFunnelQuery(resultThroughPojo, boolQueryBuilder, true, dictionaryCode);

                String stageAttr = CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.CUSTOMER_STAGE);
                boolQueryBuilder.filter(termQuery(stageAttr, dictionaryCode));

                StageFindStrategy stageFindStrategy = StageFindFactory.getStageFindStrategy(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode());
                PaasStageEntity paasStageEntity = stageFindStrategy.getByCode(corpid, resultThroughPojo.getRefId(), dictionaryCode);
                StringBuilder str = new StringBuilder();
                if (Objects.nonNull(paasStageEntity)) {
                    str.append("(" + I18nMessageUtil.getMessage(I18nStringConstant.CUSTOMER_STAGE) + "：").append(paasStageEntity.getName()).append(")");
                }
                businessTitle += str.toString();
                break;
            case CUSTOMER_TOTAL:
            case CUSTOMER_VALID:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = customerQuery4Team(resultThroughPojo, fieldTime, false);
                if (indicatorRuleEnum == IndicatorRuleEnum.CUSTOMER_VALID) {
                    //有效客户限制有效客户状态：所有启用的客户状态内，排除“无效客户”的客户状态即为全部有效客户状态
                    Set<String> typeIn = commonHelp.getCustomerTypeIn4Valid(corpid);
                    boolQueryBuilder.filter(termsQuery(CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.TYPE), typeIn));
                }
                break;
            case CUSTOMER_FOLLOW:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = boolQueryBuilder4Base(corpid);
                //从跟进记录中获取跟进的客户id
                getFollowIdfromCommunicateLimit(XbbRefTypeEnum.CUSTOMER_MANAGEMENT, resultThroughPojo, boolQueryBuilder, true);
                if (SystemCodeEnum.CUSTOMER_FOLLOW_OK == systemCodeEnum) {
                    businessTitle = systemCodeEnum.getName();
                    //跟进成交客户，在上面塞入过跟进的客户id后，再塞入成交的客户id，塞入两次客户id，会自动取交集
                    getDealCustomerIdfromContract(resultThroughPojo, boolQueryBuilder, true);
                }
                break;
            case CUSTOMER_FORGET:
                //遗忘客户直接用其指标名
                head = "";
                businessTitle = systemCodeEnum.getName();
                fieldTime = CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.LAST_CONNECT_TIME);
                boolQueryBuilder = customerQuery4Forget(systemCodeEnum, resultThroughPojo, fieldTime);
                break;
            case CUSTOMER_BIRTHDAY:
                head = "";
                businessTitle = SystemCodeEnum.CUSTOMER_BIRTHDAY_TOMORROW.getName();
                Integer startTime = DateUtil.getTodayInt();
                Integer endTime = startTime + TimeConstant.SECONDS_TWO_DAY;
                boolQueryBuilder = customerQuery4Birthday(resultThroughPojo, startTime, endTime);
                break;
            case CUSTOMER_OK:
                businessTitle = systemCodeEnum.getName();
                boolQueryBuilder = boolQueryBuilder4Base(corpid);
                //首次，复购，普通成交客户。其中首次和复购不限制开始时间，成交客户限制开始时间
                if (SystemCodeEnum.CUSTOMER_OK_HOME_FIRST == systemCodeEnum || SystemCodeEnum.CUSTOMER_OK_HOME_SECOND == systemCodeEnum) {
                    BoolQueryBuilder contractQueryBuilder = customerOkQuery(resultThroughPojo, true);
                    //只读取总值，不用切分时间桶
                    ValueTypeEnum valueTypeEnum = ValueTypeEnum.VALUE_TYPE;
                    CustomerOkFirstSecondPojo customerOkFirstSecondPojo = systemChartHelp.firstAndSecondAggregations(resultThroughPojo, contractQueryBuilder, valueTypeEnum);
                    Set<Long> customerIdIn;
                    if (SystemCodeEnum.CUSTOMER_OK_HOME_FIRST == systemCodeEnum) {
                        customerIdIn = customerOkFirstSecondPojo.getFirstCids();
                    } else {
                        customerIdIn = customerOkFirstSecondPojo.getSecondCids();
                    }
                    customerIdIn.add(-1L);
                    //客户id塞入boolQueryBuilder
                    boolQueryBuilder.filter(termsQuery("dataId", customerIdIn));
                } else {
                    //成交客户
                    businessTitle = indicatorRuleEnum.getThroughName();
                    getDealCustomerIdfromContract(resultThroughPojo, boolQueryBuilder, true);
                }
                break;
            case CUSTOMER_LOSE:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = customerQuery4Lose(resultThroughPojo);
                break;
            case CUSTOMER_ACTIVE:
                businessTitle = indicatorRuleEnum.getThroughName();
                String lastConnectTime = CustomerManagementEnum.getEsAttr4Keyword(CustomerManagementEnum.LAST_CONNECT_TIME);
                boolQueryBuilder = customerQuery4Team(resultThroughPojo, lastConnectTime, true);
                break;
            case CUSTOMER_INDUSTRY:
            case CUSTOMER_SOURCE:
            case CUSTOMER_TYPE:
                if (formId == null || formId <= 0) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), I18nMessageUtil.getMessage(CommonConstant.FORM));
                }
                if (StringUtil.isEmpty(dictionaryCode)) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), indicatorRuleEnum.getName());
                }
                boolQueryBuilder = customerQuery4Team(resultThroughPojo, "addTime", true);
                DictionaryEnum dictionaryEnum = DictionaryEnum.getEnumByIndicatorRuleEnum(indicatorRuleEnum);
                String dictionaryAttr = CustomerManagementEnum.getEsAttrByIndicatorRuleEnum(indicatorRuleEnum);
                boolQueryBuilder.filter(termQuery(dictionaryAttr, dictionaryCode));
                //查询出字典list
                List<ItemPoJo> dictionaryList = dataDictionaryModel.getDictionaryList4Saas(dictionaryEnum, corpid, formId);
                String codeTitle = commonHelp.formatThroughDictionary(dictionaryCode, indicatorRuleEnum.getName(), dictionaryList);
                businessTitle = indicatorRuleEnum.getThroughName() + codeTitle;
                boolean okFlag = SystemCodeEnum.ifCustomerDrawOk(systemCodeEnum);
                if (okFlag) {
                    getDealCustomerIdfromContract(resultThroughPojo, boolQueryBuilder, true);
                    businessTitle = I18nMessageUtil.getMessage(I18nStringConstant.CUSTOMER_OK) + codeTitle;
                }
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224056);
        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        resultThroughVo.setHead(head + businessTitle);
        return boolQueryBuilder;
    }

    /**
     * 跟进记录指标穿透查询条件封装
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/2 19:48
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder throughQuery4Communicate(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String corpid = resultThroughPojo.getCorpid();
        String businessTitle = "";
        BoolQueryBuilder boolQueryBuilder;
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        switch (indicatorRuleEnum) {
            case COMMUNICATE_ADD:
            case COMMUNICATE_TYPE:
                boolQueryBuilder = communicateQuery4UserLimitStart(resultThroughPojo);
                String codeTitle = "";
                if (IndicatorRuleEnum.COMMUNICATE_TYPE == indicatorRuleEnum) {
                    String dictionaryCode = resultThroughPojo.getDictionaryCode();
                    if (StringUtil.isEmpty(dictionaryCode)) {
                        throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, String.format(StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), indicatorRuleEnum.getName()));
                    }
                    String typeAttr = CustomerCommunicateEnum.getEsAttr4Keyword(CustomerCommunicateEnum.TYPE);
                    boolQueryBuilder.filter(termQuery(typeAttr, dictionaryCode));
                    //查询出跟进记录的表单id
                    PaasFormEntityExt paasFormEntityExt = paasFormModel.getByBusinessType(XbbRefTypeEnum.CUSTOMER_COMMUNICATE.getCode(), corpid);
                    //查询出跟进方式list
                    List<ItemPoJo> typeList = dataDictionaryModel.getDictionaryList4Saas(DictionaryEnum.COMMUNICATE_TYPE, corpid, paasFormEntityExt.getId());
                    codeTitle = commonHelp.formatThroughDictionary(dictionaryCode, indicatorRuleEnum.getName(), typeList);
                }
                businessTitle = indicatorRuleEnum.getThroughName() + codeTitle;
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224057);
        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 机会指标穿透查询条件封装
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/5 21:14
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder throughQuery4Opportunity(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String corpid = resultThroughPojo.getCorpid();
        String businessTitle = "";
        BoolQueryBuilder boolQueryBuilder;
        String fieldTime = SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.ESTIMATE_END_TIME);
        String dictionaryCode = resultThroughPojo.getDictionaryCode();
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        StageFindStrategy stageFindStrategy = StageFindFactory.getStageFindStrategy(XbbRefTypeEnum.SALES_OPPORTUNITY.getCode());
        switch (indicatorRuleEnum) {
            case OPPORTUNITY_ADD_COUNT:
            case OPPORTUNITY_ADD_AMOUNT:
            case OPPORTUNITY_ADD:
                businessTitle = indicatorRuleEnum.getThroughName();
                //查出除取消阶段之外的阶段
                //允许版本筛选、阶段筛选（阶段筛选器）
                Set<String> saleStageCodeIn = stageFindStrategy.findStageCodeList(corpid, new StageFindPojo(resultThroughPojo.getRefId(), resultThroughPojo.getArgTwoId(), StageTypeEnum.getExceptCancelType(), resultThroughPojo.getArgStrIn()));
                boolQueryBuilder = opportunityQuery4Create(resultThroughPojo, "addTime", true, saleStageCodeIn, true);
                break;
            case OPPORTUNITY_EXPIRE_COUNT:
            case OPPORTUNITY_EXPIRE_AMOUNT:
                businessTitle = indicatorRuleEnum.getThroughName();
                //允许版本筛选
                saleStageCodeIn = stageFindStrategy.findStageCodeList(corpid, new StageFindPojo(resultThroughPojo.getRefId(), resultThroughPojo.getArgTwoId(), StageTypeEnum.getOrdinaryType()));
                boolQueryBuilder = opportunityQuery4Create(resultThroughPojo, fieldTime, true, saleStageCodeIn, true);
                break;
            case OPPORTUNITY_FUNNEL:
                //销售漏斗特殊逻辑
                //允许版本筛选
                businessTitle = indicatorRuleEnum.getThroughName();
                PaasStageEntity paasStageEntity = stageFindStrategy.getByCode(corpid, resultThroughPojo.getRefId(), dictionaryCode);
                if (Objects.nonNull(paasStageEntity)) {
                    businessTitle += "(销售阶段：" + paasStageEntity.getName() + ")";
                }
                ChartEntity chartEntity = chartModel.getByKey(resultThroughPojo.getId(), resultThroughPojo.getCorpid());
                resultThroughPojo.setChartEntity(chartEntity);
                //销售漏斗查询条件封装
                boolQueryBuilder = opportunityQueryBaseForSaleFunnel(resultThroughPojo, true, Arrays.asList(dictionaryCode));
                if (StringUtil.isEmpty(dictionaryCode)) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, String.format(StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), indicatorRuleEnum.getName()));
                }
                break;
            case OPPORTUNITY_ESTIMATE:
                businessTitle = indicatorRuleEnum.getThroughName();
                //允许版本筛选（和赢单在一起）、阶段筛选（阶段筛选器）
                saleStageCodeIn = stageFindStrategy.findStageCodeList(corpid, new StageFindPojo(resultThroughPojo.getRefId(), resultThroughPojo.getArgTwoId(), StageTypeEnum.getExceptLoseCancelType(), resultThroughPojo.getArgStrIn()));
                boolQueryBuilder = opportunityQuery4Create(resultThroughPojo, fieldTime, true, saleStageCodeIn, true);
                break;
            case OPPORTUNITY_WIN:
                businessTitle = indicatorRuleEnum.getThroughName();
                saleStageCodeIn = stageFindStrategy.findStageCodeList(corpid, new StageFindPojo(resultThroughPojo.getRefId(), StageTypeEnum.getSuccessType()));
                boolQueryBuilder = opportunityQuery4Create(resultThroughPojo, fieldTime, true, saleStageCodeIn, false);
                break;
            case OPPORTUNITY_LOSE_CAUSE:
                saleStageCodeIn = stageFindStrategy.findStageCodeList(corpid, new StageFindPojo(resultThroughPojo.getRefId(), StageTypeEnum.getLoseType()));
                boolQueryBuilder = opportunityQuery4Create(resultThroughPojo, fieldTime, true, saleStageCodeIn, false);
                String codeTitle = "";
                if (StringUtil.isEmpty(dictionaryCode)) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, String.format(StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), indicatorRuleEnum.getName()));
                }
                String loseAttr = SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.WASTAGE);
                boolQueryBuilder.filter(termQuery(loseAttr, dictionaryCode));
                //查询出跟进记录的表单id
                PaasFormEntityExt paasFormEntityExt = paasFormModel.getByKey(resultThroughPojo.getRefId(), corpid);
                //查询出输单原因list
                List<ItemPoJo> loseCauseList = dataDictionaryModel.getDictionaryList4Saas(DictionaryEnum.SALE_STAGE_WASTAGE, corpid, paasFormEntityExt.getId());
                codeTitle = commonHelp.formatThroughDictionary(dictionaryCode, indicatorRuleEnum.getName(), loseCauseList);
                businessTitle = indicatorRuleEnum.getThroughName() + codeTitle;
                break;
            case OPPORTUNITY_FOLLOW:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = boolQueryBuilder4Base(corpid);
                //从跟进记录中获取跟进的机会id
                getFollowIdfromCommunicateLimit(XbbRefTypeEnum.SALES_OPPORTUNITY, resultThroughPojo, boolQueryBuilder, true);
                break;
            case OPPORTUNITY_STAGE:
                if (StringUtil.isEmpty(dictionaryCode)) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, String.format(StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), indicatorRuleEnum.getName()));
                }
                //允许版本筛选
                businessTitle = indicatorRuleEnum.getThroughName();
                paasStageEntity = stageFindStrategy.getByCode(corpid, resultThroughPojo.getRefId(), dictionaryCode);
                if (Objects.nonNull(paasStageEntity)) {
                    businessTitle += "(销售阶段：" + paasStageEntity.getName() + ")";
                };
                boolQueryBuilder = opportunityQuery4Create(resultThroughPojo, "addTime", true, Arrays.asList(dictionaryCode), false);
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224083);
        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 合同指标穿透查询条件封装
     * @param openRule 合同业绩分配是否开启
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/2 15:49
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder throughQuery4Contract(boolean openRule, SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String businessTitle = "";
        BoolQueryBuilder boolQueryBuilder;
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        switch (indicatorRuleEnum) {
            case CONTRACT_COUNT:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = contractQuery4SignNoRed(resultThroughPojo);
                break;
            case CONTRACT_AMOUNT:
            case CONTRACT_PROFIT:
            case CONTRACT_PROFIT_CASH:
                businessTitle = indicatorRuleEnum.getThroughName();
                if (openRule) {
                    //符合条件的合同查询条件：签订时间、分配人
                    boolQueryBuilder = contractQuery4performance2Parent(null, true, resultThroughPojo);
                } else {
                    boolQueryBuilder = contractQuery4Sign(null, resultThroughPojo);
                }
                break;
            case RED_CONTRACT:
                //退货合同（红冲合同），则去退货处处理
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = throughQuery4Refund(openRule, systemCodeEnum, resultThroughPojo, resultThroughVo);
                break;
            case ORDER_DISTRIBUTOR:
                //图表中心 经销商管理 订单穿透查询
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = packageOrderSearch(resultThroughPojo);
                break;
            case ORDER_PROFIT:
                //图表中心 经销商管理 订单穿透查询
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = packageOrderSearch(resultThroughPojo);
                break;
            case RETURN_DISTRIBUTOR:
                //图表中心 经销商管理 退货单穿透查询
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = packageReturnSearch(resultThroughPojo);
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224058);
        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 回款指标穿透查询条件封装
     * @param resultFromEnum 统计请求来源枚举
     * @param openRule 合同业绩分配是否开启
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/6 11:30
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder throughQuery4Payment(ResultFromEnum resultFromEnum, boolean openRule, SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String businessTitle = "";
        BoolQueryBuilder boolQueryBuilder;
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        //是否要把回款单转化为应收款查询（移动端只有应收款列表，所以还要把所有已回款关联的应收款查出）
        boolean changePlanFlag = false;
        switch (indicatorRuleEnum) {
            case PAYMENT_ED:
                businessTitle = indicatorRuleEnum.getThroughName();
                if (openRule) {
                    List<String> notBadCodeList = PaymentSheetTypeEnum.getNotBadCodeList();
                    boolQueryBuilder = paymentQuery4Performance(true, resultThroughPojo, notBadCodeList);
                } else {
                    List<String> codeList = getCodeListForPaymentSheet(resultThroughPojo);
                    boolQueryBuilder = paymentQuery4Belong(resultThroughPojo, codeList);
                }
                break;
            case WRITE_OFF:
                businessTitle = indicatorRuleEnum.getThroughName();
                List<String> writeOffCodeList = PaymentSheetTypeEnum.getWriteOffCodeList();
                if (openRule) {
                    boolQueryBuilder = paymentQuery4Performance(true, resultThroughPojo, writeOffCodeList);
                } else {
                    boolQueryBuilder = paymentQuery4Belong(resultThroughPojo, writeOffCodeList);
                }
                break;
            case PAYMENT_UN:
            case PAYMENT_TASK:
                //未收款(应收未收)本身就是应收款，无需再处理
                changePlanFlag = false;
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = paymentQuery4Un(resultThroughPojo);
                break;
            case PAYMENT_BAD:
                businessTitle = indicatorRuleEnum.getThroughName();
                //坏账不按照业绩分配统计
                List<String> badCodeList = PaymentSheetTypeEnum.getBadCodeList();
                boolQueryBuilder = paymentQuery4Belong(resultThroughPojo, badCodeList);
                break;
            case RED_SHEET:
                businessTitle = indicatorRuleEnum.getThroughName();
                if (openRule) {
                    //符合条件的合同查询条件：签订时间、分配人
                    List<String> redCodeList = PaymentSheetTypeEnum.getRedCodeList();
                    boolQueryBuilder = paymentQuery4Performance(true, resultThroughPojo, redCodeList);
                } else {
                    List<String> redCodeList = PaymentSheetTypeEnum.getRedCodeList();
                    boolQueryBuilder = paymentQuery4Belong(resultThroughPojo, redCodeList);
                }
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224059);
        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        if (changePlanFlag) {
            //需要通过回款单查出应收款：先得到回款单关联的应收款ids
            String corpid = resultThroughPojo.getCorpid();
            List<String> fieldList = Arrays.asList(PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.PAYMENT));
            //查询出表单list，用于获取业务名
            List<PaasFormDataEntityExt> list = formHelp.getFormList(corpid, boolQueryBuilder, IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET, fieldList);
            Set<Long> planId = new HashSet<>();
            planId.add(-1L);
            for (PaasFormDataEntityExt entityExt : list) {
                JSONObject data = JsonHelperUtil.getJSONObject(entityExt.getData());
                planId.add(data.getLong(PaymentSheetEnum.PAYMENT.getAttr()));
            }
            BoolQueryBuilder planQueryBuilder = boolQueryBuilder4Base(corpid, null);
            planQueryBuilder.filter(termsQuery("id", planId));
            return planQueryBuilder;
        } else {
            return boolQueryBuilder;
        }
    }

    /**
     * 联系人指标穿透查询条件封装
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/6 15:32
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder throughQuery4Contact(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Create(resultThroughPojo, "addTime");
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        String businessTitle = indicatorRuleEnum.getThroughName();
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 发票指标穿透查询条件封装
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/6 15:35
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder throughQuery4Invoice(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        String businessTitle = indicatorRuleEnum.getThroughName();
        //发票统计
        BoolQueryBuilder boolQueryBuilder = invoiceQueryBuilder(resultThroughPojo);
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 进项发票指标穿透查询条件封装
     *
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2020/3/16 15:32
     * @since v1.0
     */
    BoolQueryBuilder throughQuery4InvoicePurchase(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) {
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        String businessTitle = indicatorRuleEnum.getThroughName();
        //发票统计
        BoolQueryBuilder boolQueryBuilder = invoicePurchaseQueryBuilder(resultThroughPojo);
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 产品指标穿透查询条件封装
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/6 15:39
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder throughQuery4Product(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String businessTitle = "";
        BoolQueryBuilder boolQueryBuilder;
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();

        switch (indicatorRuleEnum) {
            case PRODUCT_SALES:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = productSalesSingleQuery4Child(resultThroughPojo, null, resultThroughPojo.getRefId());
                if (resultThroughPojo.getPerformancePk()) {
                    //如果业绩目标，则过滤设置过的考核产品
                    ChartEntity chartEntity = chartModel.getSystemChartByCode(resultThroughPojo.getCorpid(), SystemCodeEnum.PERFORMANCE_PRODUCT_SALES);
                    PerformanceSetExplainPojo explainPojo = performanceHelp.getPerformanceSetExplainPojo(chartEntity);
                    List<Long> assessProductList = explainPojo.getAccessProductIds();
                    if (assessProductList != null && assessProductList.size() > 0) {
                        boolQueryBuilder.filter(termsQuery(ContractProductSubFormEnum.getAttrConnectData(ContractProductSubFormEnum.PRODUCT_ID), assessProductList));
                    }
                }
                break;
            case ORDER_PRODUCT:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = orderProduct4Distributor(resultThroughPojo, null, resultThroughPojo.getRefId());
                break;
            case RETURN_PRODUCT:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = returnProduct4Distributor(resultThroughPojo, null, resultThroughPojo.getRefId());
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224060);
        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 退货合同（红冲合同）指标穿透查询条件封装
     * @param openRule 是否开启合同业绩分配
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/6 19:52
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder throughQuery4Refund(boolean openRule, SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String businessTitle = "";
        BoolQueryBuilder boolQueryBuilder;
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        switch (indicatorRuleEnum) {
            case RED_CONTRACT:
                businessTitle = indicatorRuleEnum.getThroughName();
                //退货合同（红冲合同）需要考虑业绩分配
                boolQueryBuilder = refundQueryBuilder(openRule, resultThroughPojo);
                break;
            case RED_REFUND_REASON:
                String dictionaryCode = resultThroughPojo.getDictionaryCode();
                if (StringUtil.isEmpty(dictionaryCode)) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, String.format(StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), indicatorRuleEnum.getName()));
                }
                boolQueryBuilder = refundQueryBuilder(false, resultThroughPojo);
                String corpid = resultThroughPojo.getCorpid();
                String codeTitle = "";
                String refundReasonAttr = RefundEnum.getEsAttr4Keyword(RefundEnum.REFUND_REASON);
                boolQueryBuilder.filter(termQuery(refundReasonAttr, dictionaryCode));
                //查询出跟进记录的表单id
                PaasFormEntityExt paasFormEntityExt = paasFormModel.getByBusinessType(XbbRefTypeEnum.REFUND.getCode(), corpid);
                //查询出退货原因数据字典list
                List<ItemPoJo> refundReasonList = dataDictionaryModel.getDictionaryList4Saas(DictionaryEnum.REFUND_REASON, corpid, paasFormEntityExt.getId());
                codeTitle = commonHelp.formatThroughDictionary(dictionaryCode, indicatorRuleEnum.getName(), refundReasonList);
                businessTitle = indicatorRuleEnum.getThroughName() + codeTitle;
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224061);
        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 付款指标穿透查询条件封装
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/6 20:23
     * @since v1.0
     * @version v1.0
     */
    BoolQueryBuilder throughQuery4Pay(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String businessTitle;
        BoolQueryBuilder boolQueryBuilder;
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        //是否要把付款单转化为付款计划查询（移动端只有付款计划列表，所以还要把所有已付款关联的付款计划查出）
        switch (indicatorRuleEnum) {
            case PAY_UN:
                //未付款本身就是付款计划，无需再处理
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = payQuery4Un(resultThroughPojo);
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224061);
        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 付款单指标穿透查询条件封装
     *
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException 异常
     * @author 徐俊杰
     * @date 2020/3/16 15:40
     * @since v1.0
     */
    BoolQueryBuilder throughQuery4PaySheet(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String businessTitle;
        BoolQueryBuilder boolQueryBuilder;
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        switch (indicatorRuleEnum) {
            case PAY_ED:
                businessTitle = indicatorRuleEnum.getThroughName();
                List<String> notBadCodeList = PaySheetTypeEnum.getNotBadCodeList();
                boolQueryBuilder = paySheetQuery(resultThroughPojo, notBadCodeList);
                break;
            case WRITE_OFF_PAY:
                businessTitle = indicatorRuleEnum.getThroughName();
                List<String> writeOffCodeList = PaySheetTypeEnum.getWriteOffCodeList();
                boolQueryBuilder = paySheetQuery(resultThroughPojo, writeOffCodeList);
                break;
            case PAY_BAD:
                businessTitle = indicatorRuleEnum.getThroughName();
                List<String> badCodeList = PaymentSheetTypeEnum.getBadCodeList();
                boolQueryBuilder = paySheetQuery(resultThroughPojo, badCodeList);
                break;
            case PAY_RED:
                businessTitle = indicatorRuleEnum.getThroughName();
                List<String> redCodeList = PaySheetTypeEnum.getRedCodeList();
                boolQueryBuilder = paySheetQuery(resultThroughPojo, redCodeList);
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224067);
        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 采购合同指标穿透查询条件封装
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/6 20:32
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder throughQuery4Purchase(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String businessTitle = "";
        BoolQueryBuilder boolQueryBuilder;
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        switch (indicatorRuleEnum) {
            case PURCHASE_COUNT:
            case PURCHASE_AMOUNT:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = purchaseQuery(resultThroughPojo);
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224062);
        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 生产单指标穿透查询条件封装
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/6 21:43
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder throughQuery4ProductionOrder(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String businessTitle = "";
        BoolQueryBuilder boolQueryBuilder;
        Script script = null;
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        switch (indicatorRuleEnum) {
            case PRODUCTION_ORDER_ADD:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = boolQueryBuilder4Create(resultThroughPojo, "addTime");
                //无需审批和通过
                boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
                break;
            case PRODUCTION_ORDER_TOTAL:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = productionOrderQuery4Complete(resultThroughPojo, script);
                break;
            case PRODUCTION_ORDER_FINISH_INTIME:
                businessTitle = indicatorRuleEnum.getThroughName();
                String planTime = ProductionOrderEnum.getEsAttr4Keyword(ProductionOrderEnum.PLAN_TIME);
                String completeTime = ProductionOrderEnum.getEsAttr4Keyword(ProductionOrderEnum.COMPLETE_TIME);
                /*
                完成时间为空或计划时间 >= 完成时间为未超时
                "doc['data.date_1'].value - doc['data.date_2'].value >= 0"
                */
                script = ScriptHelp.minusFilter4Egt(planTime, completeTime, "0");
                boolQueryBuilder = productionOrderQuery4Complete(resultThroughPojo, script);
                break;
            case PRODUCTION_ORDER_FINISH_DELAY:
                businessTitle = indicatorRuleEnum.getThroughName();
                planTime = ProductionOrderEnum.getEsAttr4Keyword(ProductionOrderEnum.PLAN_TIME);
                completeTime = ProductionOrderEnum.getEsAttr4Keyword(ProductionOrderEnum.COMPLETE_TIME);
                /*
                已完成超时
                "doc['data.date_1'].value - doc['data.date_2'].value < 0"
                */
                script = ScriptHelp.minusFilter4Lt(planTime, completeTime, "0");
                boolQueryBuilder = productionOrderQuery4Complete(resultThroughPojo, script);
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224063);
        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 工单指标穿透查询条件封装
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/5/7 18:05
     * @since v1.0
     */
    public BoolQueryBuilder throughQuery4WorkOrder(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String businessTitle = "";
        BoolQueryBuilder boolQueryBuilder;
        String actualTime = "actualTime";
        String expectedTime = "expectedTime";
        //额外超时时间
        String extratime = String.valueOf(WorkOrderConstant.EXTRATIME);
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        switch (indicatorRuleEnum) {
            case WORK_ORDER_TOTAL:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = workOrderQuery4LimitStart(resultThroughPojo, "addTime", false, null, null);
                break;
            case WORK_ORDER_ADD:
            case WORK_ORDER_PRICE:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = workOrderQuery4Base(resultThroughPojo, "addTime", null, null);
                break;
            case WORK_ORDER_DEGREE:
                String dictionaryCode = resultThroughPojo.getDictionaryCode();
                if (StringUtil.isEmpty(dictionaryCode)) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), WorkOrderEnum.RATE_STAR.getAttrName());
                }
                boolQueryBuilder = workOrderQuery4Base(resultThroughPojo, WorkOrderEnum.COMPLETE_TIME.getAttr(), WorkOrderStatusEnum.FINISH.getCode(), null);
                String starAttr = WorkOrderEnum.getEsAttr4Keyword(WorkOrderEnum.RATE_STAR);
                boolQueryBuilder.filter(termQuery(starAttr, dictionaryCode));
                //工单的星级，es内初始化的是double型
                Double starDouble = StringUtil.toDouble(dictionaryCode, 0D);
                Integer star = starDouble.intValue();
                StringBuilder str = new StringBuilder();
                str.append("(" + I18nMessageUtil.getMessage(CommonConstant.STAR) + "：").append(StarEnum.getTextByValue(star)).append(")");
                businessTitle = indicatorRuleEnum.getThroughName() + str.toString();
                break;
            case WORK_ORDER_CLOSE:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = workOrderQuery4Base(resultThroughPojo, "closeTime", WorkOrderStatusEnum.CLOSE.getCode(), null);
                break;
            case WORK_ORDER_FINISH:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = workOrderQuery4Base(resultThroughPojo, "endTime", WorkOrderStatusEnum.FINISH.getCode(), null);
                break;
            case WORK_ORDER_FINISH_DELAY:
                businessTitle = indicatorRuleEnum.getThroughName();
                //实际耗时 - 预计耗时 > 额外超时时间
                Script script = ScriptHelp.minusFilter4Gt(actualTime, expectedTime, extratime);
                boolQueryBuilder = workOrderQuery4Base(resultThroughPojo, "endTime", WorkOrderStatusEnum.FINISH.getCode(), script);
                boolQueryBuilder.mustNot(termQuery(expectedTime, WorkOrderConstant.EXPECTED_TIME_ZERO));
                break;
            case WORK_ORDER_FINISH_INTIME:
                businessTitle = indicatorRuleEnum.getThroughName();
                //实际耗时 - 预计耗时 <= 额外超时时间
                script = ScriptHelp.minusFilter4Elt(actualTime, expectedTime, extratime);
                boolQueryBuilder = workOrderQuery4Base(resultThroughPojo, "endTime", WorkOrderStatusEnum.FINISH.getCode(), script);
                boolQueryBuilder.mustNot(termQuery(expectedTime, WorkOrderConstant.EXPECTED_TIME_ZERO));
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224064);
        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 报价单穿透查询条件封装
     *
     * @param systemCodeEnum 系统指标枚举
     * @param resultThroughPojo 穿透pojo
     * @param resultThroughVo 穿透vo，此方法内只设置其head
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException 异常
     * @author 徐俊杰
     * @date 2019/8/26 16:31
     * @since v1.0
     */
    BoolQueryBuilder throughQuery4Quotation(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String businessTitle;
        BoolQueryBuilder quotationQuery;
        String corpid = resultThroughPojo.getCorpid();
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        switch (indicatorRuleEnum) {
            case QUOTATION_COUNT:
            case QUOTATION_AMOUNT:
                businessTitle = indicatorRuleEnum.getThroughName();
                quotationQuery = quotationCountQuery(resultThroughPojo);
                break;
            case QUOTATION_CONVERSION:
                businessTitle = indicatorRuleEnum.getThroughName();
                quotationQuery = quotationCreateContractCountQuery(resultThroughPojo);
                break;
            case QUOTATION_CUSTOMER_COUNT:
                quotationQuery = boolQueryBuilder4Base(corpid);
                businessTitle = indicatorRuleEnum.getThroughName();
                getQuotationCustomer(resultThroughPojo, quotationQuery);
                break;
            default:
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224058);
        }
        //头部标题，最终需要封装成如此格式：“全公司2019-08-01到2019-08-31新增报价单数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return quotationQuery;
    }

    //-------------------------------全景图数据统计相关查询条件-------------------------------
    /**
     * 全景图常用查询条件：
     * （1）限制corpid，del，业务id（如客户id、合同id等 具体字段名通过dataAttr传入）
     * （2）获取当前登录人及其下属的数据，按照【业务的userAttr字段（如创建人）】进行限制
     * （3）不限制时间
     * @param panoramaPojo 全景图参数pojo
     * @param userAttr 限制人的字段，如创建人、签订人等
     * @param dataAttr 关联字段名，如跟进记录关联的客户字段、合同关联的客户字段、回款关联的客户字段等
     * @param dataId 业务id
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/6/3 15:20
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder panoramaQuery4Base(PanoramaPojo panoramaPojo, String userAttr, String dataAttr, Long dataId) {
        /*--------处理查询条件--------*/
        //限制基础条件：corpid、del
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(panoramaPojo.getCorpid());
        //限制业务id
        boolQueryBuilder.filter(termQuery(dataAttr, dataId));
        //限制人（获取到自己及其下属的数据）
        if (userAttr != null && chartDtoHelp.judgePermissionAndIfCheck(panoramaPojo)) {
            boolQueryBuilder.filter(termsQuery(userAttr, panoramaPojo.getUserIdIn()));
        }
        return boolQueryBuilder;
    }

    /**
     * 全景图内【通话记录相关】查询条件封装：
     * （1）与基础查询panoramaQuery4Base方法一致的条件
     * （2）还需要限制通话的refType，此处refType为客户
     * @param panoramaPojo 全景图参数pojo
     * @param refId 关联业务id
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/6/3 16:49
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder panoramaQuery4Call(PanoramaPojo panoramaPojo, Long refId) {
        /*--------处理查询条件--------*/
        //限制基础条件：corpid、del
        BoolQueryBuilder boolQueryBuilder = panoramaQuery4Base(panoramaPojo, "userId.keyword", "refId", refId);
        //限制业务类型
        int code = XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode();
        boolQueryBuilder.filter(rangeQuery("refType").gte(code).lte(code));
        return boolQueryBuilder;
    }

    /**
     * 全景图【限制业务团队】查询条件：
     * （1）限制corpid，del，业务id（如客户id、合同id等 具体字段名通过dataAttr传入）
     * （2）获取当前登录人及其下属的数据，按照【业务的团队】进行限制，且不限制isMain
     * （3）不限制时间
     * @param panoramaPojo 全景图参数pojo
     * @param dataAttr 关联字段名，如跟进记录关联的客户字段、合同关联的客户字段、回款关联的客户字段等
     * @param dataId 业务id
     * @param teamIndexTypeEnum 团队索引
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/6/3 21:28
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder panoramaQuery4Team(PanoramaPojo panoramaPojo, String dataAttr, Long dataId, IndexTypeEnum teamIndexTypeEnum) {
        String corpid = panoramaPojo.getCorpid();
        /*--------处理查询条件--------*/
        //限制基础条件：corpid、del
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(corpid);
        //限制业务id
        boolQueryBuilder.filter(termQuery(dataAttr, dataId));
        //负责人处理，获取到登录员工及其下属负责的数据
        if (chartDtoHelp.judgePermissionAndIfCheck(panoramaPojo)) {
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(teamIndexTypeEnum, corpid, null, panoramaPojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 全景图内【合同相关指标（成交次数/金额、复购周期、距离上次成交时长）】查询条件封装：
     * @param panoramaPojo 全景图参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/7/8 17:01
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder panoramaQuery4Contract(PanoramaPojo panoramaPojo) {
        /*--------处理查询条件--------*/
        //不排除特定合同状态，限制负责人，且不限制时间
        String dataAttr = ContractEnum.getEsAttr4Keyword(ContractEnum.LINK_CUSTOMER);
        BoolQueryBuilder boolQueryBuilder = panoramaQuery4Team(panoramaPojo, dataAttr, panoramaPojo.getDataId(), IndexTypeEnum.IDX_SAAS_CONTRACT_TEAM);
        //排除红冲
        String isRedAttr = ContractEnum.getEsAttr4Keyword(ContractEnum.IS_RED);
        boolQueryBuilder.filter(termQuery(isRedAttr, BasicConstant.ZERO));
        return boolQueryBuilder;
    }

    /**
     * 全景图内【应收款】查询条件封装：
     * （1）与限制业务团队的panoramaQuery4Team方法一致的条件
     * （2）限制应收款状态
     * @param panoramaPojo 全景图参数pojo
     * @param dataId 业务id
     * @param statusIn 回款状态数组
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/6/3 22:00
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder panoramaQuery4PaymentUn(PanoramaPojo panoramaPojo, Long dataId, List<String> statusIn) {
        //限制业务id
        String dataAttr = PaymentEnum.getEsAttr4Keyword(PaymentEnum.CUSTOMER);
        /*--------处理查询条件--------*/
        //限制基础条件：corpid、del
        BoolQueryBuilder boolQueryBuilder = panoramaQuery4Team(panoramaPojo, dataAttr, dataId, IndexTypeEnum.IDX_SAAS_PAYMENT_TEAM);
        if (statusIn != null) {
            //限制应收款的状态
            String statusField = PaymentEnum.getEsAttr4Keyword(PaymentEnum.STATUS);
            boolQueryBuilder.filter(termsQuery(statusField, statusIn));
        }
        return boolQueryBuilder;
    }

    /**
     * 全景图，已回款、红冲、坏账查询条件封装，因这些都是存入的回款单
     *
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/6/4 11:28
     * @version v1.0
     * @since v1.0
     */
    public BoolQueryBuilder panoramaQuery4PaymentEd(PanoramaPojo panoramaPojo, List<String> typeList) {
        //限制基础条件
        String dataAttr = PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.CUSTOMER_ID);
        Long dataId = panoramaPojo.getDataId();
        BoolQueryBuilder boolQueryBuilder = panoramaQuery4Base(panoramaPojo, null, dataAttr, dataId);
        //处理类型
        String typeAttr = PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.PAYMENT_SHEET_TYPE);
        boolQueryBuilder.filter(termsQuery(typeAttr, typeList));
        //不拿子回款单
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), SheetAloneEnum.getNotChildCodeList()));
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 全景图-报价单查询条件
     *
     * @param panoramaPojo 全景图参数pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/8/26 16:22
     * @since v1.0
     */
    public BoolQueryBuilder panoramaQuery4Quotation(PanoramaPojo panoramaPojo) {
        String dataAttr = QuotationEnum.getEsAttr4Keyword(QuotationEnum.CUSTOMER_ID);
        BoolQueryBuilder quotationQuery = panoramaQuery4Team(panoramaPojo, dataAttr, panoramaPojo.getDataId(), IndexTypeEnum.IDX_SAAS_CONTRACT_TEAM);
        //最新版本
        String isNew = QuotationEnum.getEsAttr4Keyword(QuotationEnum.IS_NEW);
        quotationQuery.filter(termQuery(isNew, 1));
        return quotationQuery;
    }

    /**
     * 全景图发票查询条件处理
     *
     * @param panoramaPojo 全景图入参
     * @param isCancel 是否作废
     * @param isRed 是否红冲
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/11/27 15:49
     * @since v1.0
     */
    public BoolQueryBuilder panoramaQuery4Invoice(PanoramaPojo panoramaPojo, Integer isCancel, Integer isRed) {
        String userAttr = InvoiceEnum.APPLY_USER.getAttr() + ".keyword";
        String dataAttr = InvoiceEnum.getEsAttr4Keyword(InvoiceEnum.CUSTOMER_NAME);
        Long dataId = panoramaPojo.getDataId();
        BoolQueryBuilder invoiceQuery = panoramaQuery4Base(panoramaPojo, userAttr, dataAttr, dataId);
        //处理红冲、作废
        dealInvoiceRedCancel(isCancel, isRed, invoiceQuery);
        //无需审批和通过
        invoiceQuery.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return invoiceQuery;
    }

    //-------------------------------全·景图提示相关查询条件-------------------------------
    /**
     * 提示-客户-合同
     *
     * @param panoramaPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author 徐俊杰
     * @date 2019/6/10 11:12
     * @since v1.0
     */
    public BoolQueryBuilder remindQuery4Contract(PanoramaPojo panoramaPojo) throws XbbException {
        int startTime = DateUtil.getTodayInt();
        int endTime = startTime + TimeConstant.SECONDS_PER_MONTH;
        //客户id
        String dataAttr = ContractEnum.getEsAttr4Keyword(ContractEnum.LINK_CUSTOMER);
        //到期日期
        String fieldTime = ContractEnum.getEsAttr4Keyword(ContractEnum.DEADLINE);
        //只获取参与统计的合同状态---合同状态不限制formId
        List<String> statusIn = dataDictionaryModel.statisticTypeList(DictionaryEnum.CONTRACT_STATUS, panoramaPojo.getCorpid(), null);

        BoolQueryBuilder contractQuery = panoramaQuery4Team(panoramaPojo, dataAttr, panoramaPojo.getDataId(), IndexTypeEnum.IDX_SAAS_CONTRACT_TEAM);
        contractQuery.filter(rangeQuery(fieldTime).gte(startTime).lt(endTime));
        String statusAttr = ContractEnum.getEsAttr4Keyword(ContractEnum.STATUS);
        contractQuery.filter(termsQuery(statusAttr, statusIn));
        return contractQuery;
    }

    /**
     * 提示-客户-机会
     *
     * @param panoramaPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/6/4 14:18
     * @since v1.0
     */
    public BoolQueryBuilder remindQuery4Opportunity(PanoramaPojo panoramaPojo) {
        int startTime = DateUtil.getTodayInt();
        int endTime = startTime + TimeConstant.SECONDS_PER_MONTH;
        //客户id
        String dataAttr = SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.CUSTOMER_NAME);
        //预计时间
        String fieldTime = SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.ESTIMATE_END_TIME);
        //销售阶段
       /* List<Integer> typeIn = SaleStageTypeEnum.getCode4Estimate();
        List<Integer> saleStageCodeIn = saleStageModel.statisticStageCodeIn(panoramaPojo.getCorpid(), null, typeIn);*/

        BoolQueryBuilder opportunityQuery = panoramaQuery4Team(panoramaPojo, dataAttr, panoramaPojo.getDataId(), IndexTypeEnum.IDX_SAAS_OPPORTUNITY_TEAM);
        opportunityQuery.filter(rangeQuery(fieldTime).gte(startTime).lt(endTime));
        StageFindStrategy stageFindStrategy = StageFindFactory.getStageFindStrategy(XbbRefTypeEnum.SALES_OPPORTUNITY.getCode());
        Set<String> saleStageCodeIn = stageFindStrategy.findStageCodeList(panoramaPojo.getCorpid(), new StageFindPojo(StageTypeEnum.getExceptLoseCancelType()));
        opportunityQuery.filter(termsQuery(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.SALE_STAGE), saleStageCodeIn));
        return opportunityQuery;
    }

    /**
     * 提示-客户-回款
     *
     * @param panoramaPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/6/10 14:16
     * @since v1.0
     */
    public BoolQueryBuilder remindQuery4Payment(PanoramaPojo panoramaPojo) {
        int startTime = DateUtil.getInt();
        int endTime = startTime + TimeConstant.SECONDS_PER_MONTH;
        List<String> statusIn = PaymentStatusEnum.getUnStatusIn();
        //到期日期
        String fieldTime = PaymentEnum.getEsAttr4Keyword(PaymentEnum.ESTIMATE_TIME);

        BoolQueryBuilder paymentQuery = panoramaQuery4PaymentUn(panoramaPojo, panoramaPojo.getDataId(), statusIn);
        paymentQuery.filter(rangeQuery(fieldTime).gte(startTime).lt(endTime));
        return paymentQuery;
    }

    /**
     * 全景图提示-访客计划-查询参数封装
     *
     * @param panoramaPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/6/6 11:00
     * @since v1.0
     */
    public BoolQueryBuilder remindQuery4CommunicatePlan(PanoramaPojo panoramaPojo) {
        int endTime = DateUtil.getInt();
        int startTime = endTime - TimeConstant.SECONDS_PER_WEEK;
        List<String> statusIn = Collections.singletonList(CommunicatePlanStatusEnum.NO_FOLLOW.getCode());
        //创建人
        String userAttr = CommunicatePlanEnum.CREATOR_ID.getAttr() + ".keyword";
        //客户id
        String dataAttr = CommunicatePlanEnum.getEsAttr4Keyword(CommunicatePlanEnum.LINK_CUSTOMER);
        //执行时间
        String fieldTime = CommunicatePlanEnum.getEsAttr4Keyword(CommunicatePlanEnum.COMMUNICATE_TIME);
        String statusAttr = CommunicatePlanEnum.getEsAttr4Keyword(CommunicatePlanEnum.STATUS);

        BoolQueryBuilder communicatePlanQuery = panoramaQuery4Base(panoramaPojo, userAttr, dataAttr, panoramaPojo.getDataId());
        communicatePlanQuery.filter(rangeQuery(fieldTime).gte(startTime).lt(endTime));
        communicatePlanQuery.filter(termsQuery(statusAttr, statusIn));
        //无需审批和通过
        communicatePlanQuery.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return communicatePlanQuery;
    }

    /**
     * 提示-客户-报价单
     *
     * @param panoramaPojo 入参
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/8/26 17:11
     * @since v1.0
     */
    public BoolQueryBuilder remindQuery4Quotation(PanoramaPojo panoramaPojo) {
        int startTime = DateUtil.getTodayInt();
        int endTime = startTime + TimeConstant.SECONDS_PER_MONTH;
        //客户id
        String dataAttr = QuotationEnum.getEsAttr4Keyword(QuotationEnum.CUSTOMER_ID);
        //有效日期
        String fieldTime = QuotationEnum.getEsAttr4Keyword(QuotationEnum.EFFECTIVE_TIME);
        String isNew = QuotationEnum.getEsAttr4Keyword(QuotationEnum.IS_NEW);
        BoolQueryBuilder quotationQuery = panoramaQuery4Team(panoramaPojo, dataAttr, panoramaPojo.getDataId(), IndexTypeEnum.IDX_SAAS_QUOTATION_TEAM);
        quotationQuery.filter(rangeQuery(fieldTime).gte(startTime).lt(endTime));
        quotationQuery.filter(termQuery(isNew, 1));
        return quotationQuery;
    }

    /**
     * 根据是否是PK榜拿不同的回款单类型
     *
     * @param chartResultPojo 参数，判断是否是PK
     * @return java.util.List<java.lang.String>
     * @author 徐俊杰
     * @date 2019/12/27 14:52
     * @since v1.0
     */
    public List<String> getCodeListForPaymentSheet(ChartResultPojo chartResultPojo) {
        Boolean performancePk = chartResultPojo.getPerformancePk();
        if (performancePk) {
            return PaymentSheetTypeEnum.getCodeListForPK();
        } else {
            return PaymentSheetTypeEnum.getNotBadCodeList();
        }
    }

    //-------------------------------private方法-------------------------------

    /**
     * 合同相关统计的共有筛选条件：corpid、del、signTime（根据limitStart确定是否限制开始时间）、统计状态、formId、是否红冲
     * @param userFlag 是否在查询时限制签订人
     * @param isRed 是否红冲，null不作为筛选条件，1是0否
     * @param chartResultBasePojo 基础参数pojo
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @date 2019/3/30 19:18
     * @since v1.0
     * @version v1.0
     */
    private BoolQueryBuilder contractQuery4Base(boolean userFlag, Integer isRed, ChartResultBasePojo chartResultBasePojo, boolean limitStart) throws XbbException {
        String corpid = chartResultBasePojo.getCorpid();
        //时间筛选
        String fieldTime = ContractEnum.getEsAttr4Keyword(ContractEnum.SIGN_TIME);
        //调用模板筛选的基础方法
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Form(chartResultBasePojo, fieldTime, limitStart);
        //只获取参与统计的合同状态---合同状态不限制formId
        List<String> statusIn = dataDictionaryModel.statisticTypeList(DictionaryEnum.CONTRACT_STATUS, corpid, null);
        boolQueryBuilder.filter(termsQuery(ContractEnum.getEsAttr4Keyword(ContractEnum.STATUS), statusIn));
        //限制签订人id、且非主管或筛选
        if (userFlag && chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            boolQueryBuilder.filter(termsQuery(ContractEnum.getEsAttr4Keyword(ContractEnum.SIGN_PERSON), chartResultBasePojo.getUserIdIn()));
        }
        if (isRed != null) {
            String isRedAttr = ContractEnum.getEsAttr4Keyword(ContractEnum.IS_RED);
            boolQueryBuilder.filter(termQuery(isRedAttr, isRed));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }


    /**
     * 所有机会相关统计的共有筛选条件：公司id、del、时间(字段从外部传入)、阶段
     * @param chartResultBasePojo 基础参数pojo
     * @param fieldTime 筛选时间字段，如创建时间、预计结束时间
     * @param limitStart 是否限制开始时间，true限制开始时间，false不限制开始时间（区域分布）
     * @param saleStageCodeIn 阶段
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @date 2019/3/19 21:34
     * @since v1.0
     * @version v1.0
     */
    private BoolQueryBuilder opportunityQueryBase(ChartResultBasePojo chartResultBasePojo, String fieldTime, boolean limitStart, Collection<String> saleStageCodeIn){
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Time(chartResultBasePojo, fieldTime, limitStart);
        //阶段id
        if (CollectionUtils.isNotEmpty(saleStageCodeIn)) {
            boolQueryBuilder.filter(termsQuery(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.SALE_STAGE), saleStageCodeIn));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 销售漏斗es查询入参组装（穿透那边过来的 chartResultBasePojo 中的 chartEntity 需要单独查出来 ）
     * @param chartResultBasePojo
     * @param
     * @param limitStart
     * @param saleStageCodeIn
     * @return
     */
    public BoolQueryBuilder opportunityQueryBaseForSaleFunnel(ChartResultBasePojo chartResultBasePojo , boolean limitStart, List<String> saleStageCodeIn) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(chartResultBasePojo.getCorpid());
        Integer startTime = chartResultBasePojo.getStartTime();
        Integer endTime = chartResultBasePojo.getEndTime();
        ChartEntity chartEntity = chartResultBasePojo.getChartEntity();
        String corpid = chartResultBasePojo.getCorpid();
        //首页，图表中心，穿透 的销售漏斗的相关的查询里面参数 refId 对应的业务是  formId
        Long formId = chartResultBasePojo.getRefId();
        //兼容空： 图表的 timeAttr 字段为空的话，则统计规则字段取预计结束时间，否则取得是 字段的值
         String timeAttr = (Objects.isNull(chartEntity) || Objects.isNull(chartEntity.getTimeAttr())) ? SalesOpportunityEnum.ESTIMATE_END_TIME.getAttr() : chartEntity.getTimeAttr().getString("attr");
        //如果统计的字段，在机会模板那里是禁用的，那么不查询数据 (销售漏斗的入参refId 对于的业务是formId)
        if(IsOpenRuleField(corpid,formId,timeAttr)){
            boolQueryBuilder.filter(termQuery("id", -1));
            return boolQueryBuilder;
        }
        // updateTime作为统计维度
        if(SalesOpportunityEnum.UPDATE_TIME.getAttr().equals(timeAttr)){
            //时间字段是 更新时间的话 ，统计的是 最新一次阶段跳转的更新时间在这个范围内的 销售机会，这里将它转为符合条件的id
            Map<String ,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("isNow", 1);
            //是否开启了时间筛选
            if (limitStart) {
                param.put("startTime", startTime);
            }
            //其实formId在销售漏斗查询数据时，应该是要必须要传输。但是入参是没有空校验的，加个判空稳一手
            if(Objects.nonNull(formId)){
                param.put("formId",formId);
            }
            param.put("endTime", endTime);
            param.put("del", DelEnum.NORMAL.getDel());
            List<Long> opportunityIds = opportunityStageLogModel.getLastStageLogIdsByIntoTime(param);
            //如果 opportunityIds 为空 ,说明没有符合条件的机会，传输-1限制
            if(CollectionUtils.isEmpty(opportunityIds)){
                boolQueryBuilder.filter(termQuery("id", -1));
            }else {
                boolQueryBuilder.filter(termsQuery("id", opportunityIds));
            }
        }else{
            //如果是 创建时间 和  预计结束时间 ,或者最后跟进时间date_2的话 ，则走正常的时间筛选(创建时间addTime筛选前面不要加data.前缀)
            String fieldTime = SalesOpportunityEnum.ADD_TIME.getAttr().equals(timeAttr) ? timeAttr : "data."+timeAttr;
            if (limitStart) {
                //如果该值为true，则限制开始时间；如果该值为fale，则不限制开始时间。部分特殊场景不限制开始时间，如销售机会区域分布统计等
                boolQueryBuilder.filter(rangeQuery(fieldTime).gte(startTime));
            }
            boolQueryBuilder.filter(rangeQuery(fieldTime).lt(endTime));
        }
        //阶段id
        if (CollectionUtils.isNotEmpty(saleStageCodeIn)){
            boolQueryBuilder.filter(termsQuery(SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.SALE_STAGE), saleStageCodeIn));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));

        // 之前图表不支持多人维度，所以默认取了创建人字段，作为临时过渡方案; 现在按照设置的归属人来统计。(没有设置归属人的，就按照“创建人”来统计)
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            //多人维度 查询条件封装
            setBelongRule(chartResultBasePojo , boolQueryBuilder);
        }
        if (Objects.nonNull(chartResultBasePojo.getRefId()) && !Objects.equals(chartResultBasePojo.getRefId(), 0L) && !Objects.equals(chartResultBasePojo.getRefId(), -1L)){
            boolQueryBuilder.filter(termQuery("formId", chartResultBasePojo.getRefId()));
        }
        return boolQueryBuilder;
    }
    private void setBelongRule(ChartResultBasePojo  chartResultBasePojo , BoolQueryBuilder  boolQueryBuilder ) throws XbbException {
         ChartEntity chartEntity = chartResultBasePojo.getChartEntity();
         List<String> userIdIn = chartResultBasePojo.getUserIdIn();
         String corpid = chartResultBasePojo.getCorpid();
         //销售漏斗这边的 RefId 是模板 Id
         Long formId = chartResultBasePojo.getRefId();
        //图表归属人统计字段为空，或者是创建人维度。
        if(Objects.isNull(chartEntity) || Objects.isNull(chartEntity.getBelongAttr()) || SalesOpportunityEnum.CREATOR_ID.getAttr().equals(chartEntity.getBelongAttr().getString("attr"))){
            boolQueryBuilder.filter(termsQuery("creatorId.keyword",userIdIn));//text字段类型需要.keyword
            //如果统计的字段是禁用的，传输-1，那么不查询数据 (eg: 漏斗统计规则的"归属人"是"创建时间"，但是后面对应的机会模板的'创建时间"被禁用了，那么漏斗统计不到数据)
            if(IsOpenRuleField(corpid ,formId,"creatorId")){
                boolQueryBuilder.filter(termQuery("id", -1));
            }
            return;
        }
        //如果统计的字段是禁用的，传输-1，那么不查询数据 (eg: 漏斗统计规则的"归属人"是"创建时间"，但是后面对应的机会模板的'创建时间"被禁用了，那么漏斗统计不到数据)
        String belongAttr = chartEntity.getBelongAttr().getString("attr");
        if(IsOpenRuleField(corpid ,formId,belongAttr)){
            boolQueryBuilder.filter(termQuery("id", -1));
            return;
        }
        //销售机会上面关联的负责人，协同人的信息是保存在 es的 /bi_idx_saas_opportunity/my_team ,是属于"/bi_idx_saas_opportunity/saas_opportunity"的子级。所以需要关联查询
        //提示： 负责人，协同人对于的数据库是  tb_saas_opportunity_user 表。统计规则默认先给-1
        Integer rule = -1;
        //负责人 is_main 字段值  为  1
        if(SalesOpportunityEnum.OWNER_ID.getAttr().equals(belongAttr)){
            rule = BasicConstant.MAIN_USER;
        }
        //协同人 is_main 字段值  为  0
        if(SalesOpportunityEnum.COUSER_ID.getAttr().equals(belongAttr)){
            rule = BasicConstant.COOP_USER;
        }
        HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_OPPORTUNITY_TEAM, chartEntity.getCorpid(), rule , userIdIn);
        boolQueryBuilder.filter(hasChildQueryBuilder);
    }

    /**
     * 判断销售漏斗的统计规则字段是否禁用。返回true，表示字段不存在或者被禁用
     * @return
     */
    private Boolean IsOpenRuleField(String corpid , Long formId , String attr) throws XbbException {
        //兼容有些地方不传输formId
        if(Objects.isNull(formId)){
            return false;
        }
        //如果传输了formId，但是查询不到解释，这种是异常情况，返回true
        PaasFormExplainEntity explainEntity = paasFormExplainModel.getByFormId(formId,corpid );
        if(Objects.isNull(explainEntity)){
            return true;
        }
        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(explainEntity.getExplains(), attr);
        FieldAttrEntity fieldAttrEntity = explainMap.get(attr);
        //字段不存在或者被禁用
        if(Objects.isNull(fieldAttrEntity)  ||  Objects.equals(fieldAttrEntity.getIsOpen(), 0)){
            return true;
        }
        return false;
    }



    /**
     * 报价单基础ES查询条件
     *
     * @param basePojo 基础pojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author 徐俊杰
     * @date 2019/8/15 15:55
     * @since v1.0
     */
    private BoolQueryBuilder quotationQuery4Base(ChartResultBasePojo basePojo) {
        //时间筛选
        String fieldTime = QuotationEnum.getEsAttr4Keyword(QuotationEnum.QUOTATION_TIME);
        //基本条件corpid,del,time
        BoolQueryBuilder quotationQuery = boolQueryBuilder4Time(basePojo, fieldTime, true);
        //报价人筛选
        if (chartDtoHelp.judgePermissionAndIfCheck(basePojo)) {
            quotationQuery.filter(termsQuery(QuotationEnum.getEsAttr4Keyword(QuotationEnum.QUOTATION_PERSON), basePojo.getUserIdIn()));
        }
        //todo 用枚举
        quotationQuery.filter(termQuery(QuotationEnum.getEsAttr4Keyword(QuotationEnum.IS_NEW), 1));
        //todo 用枚举
        quotationQuery.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return quotationQuery;
    }

    /**
     * 处理红冲和作废发票
     *
     * @param isCancel 是否作废 null不限制该条件，1作废0否
     * @param isRed 是否红冲 null不限制该条件，1红冲0否
     * @param boolQueryBuilder 查询条件
     * @author 徐俊杰
     * @date 2019/11/27 15:44
     * @since v1.0
     */
    private void dealInvoiceRedCancel(Integer isCancel, Integer isRed, BoolQueryBuilder boolQueryBuilder) {
        if (isCancel != null) {
            //isBad字段
            String isCancelAttr = InvoiceEnum.getEsAttr4Keyword(InvoiceEnum.IS_CANCEL);
            //坏账or非坏账
            boolQueryBuilder.filter(termQuery(isCancelAttr, isCancel));
        }
        if (isRed != null) {
            //isRed字段
            String isRedAttr = InvoiceEnum.getEsAttr4Keyword(InvoiceEnum.IS_RED);
            //红冲or非红冲
            boolQueryBuilder.filter(termQuery(isRedAttr, isRed));
        }
    }

    /**
     * 客户团队
     *
     * @param chartResultPojo 入参
     * @param boolQueryBuilder 查询条件
     * @author 徐俊杰
     * @date 2019/6/14 19:38
     * @since v1.0
     */
    private void customerTeam(ChartResultPojo chartResultPojo, BoolQueryBuilder boolQueryBuilder) {
        //负责人处理
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            String corpid = chartResultPojo.getCorpid();
            IndexTypeEnum customerTeamIndex = indexTypeModel.getCustomerTeamIndex(corpid, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), SaasMarkEnum.SAAS.getCode());
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(customerTeamIndex, corpid, BasicConstant.MAIN_USER, chartResultPojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
    }

    /**
     * 订单团队
     *
     * @param chartResultPojo 入参
     * @param boolQueryBuilder 查询条件
     * @author weiming
     * @date 2021/1/21 19:38
     * @since v1.0
     */
    private void orderTeam(ChartResultPojo chartResultPojo, BoolQueryBuilder boolQueryBuilder) {
        //负责人处理
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            boolQueryBuilder.filter(termsQuery(OrderEnum.getAttrConnectData(OrderEnum.SIGN_PERSON), chartResultPojo.getUserIdIn()));
        }
    }

    /**
     * 退货单团队
     *
     * @param chartResultPojo 入参
     * @param boolQueryBuilder 查询条件
     * @author weiming
     * @date 2021/1/21 19:38
     * @since v1.0
     */
    private void returnTeam(ChartResultPojo chartResultPojo, BoolQueryBuilder boolQueryBuilder) throws XbbException {
        //负责人处理
        // 该表单的数据权限
        TransmittableThreadLocalUtil.setValue(SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.REFUND.getCode(), null, DistributorMarkEnum.DISTRIBUTOR.getCode());
        List<ConditionsEntityExt> conditionsList = new ArrayList<>();
        saasListDataPermissionHelp.listDataPermission(conditionsList,chartResultPojo.getLoginUser(), XbbRefTypeEnum.REFUND.getCode(), XbbRefTypeEnum.REFUND.getCode());
        if (CollectionUtils.isNotEmpty(conditionsList)){
            EsUtil.parseCondition(boolQueryBuilder,conditionsList,"",chartResultPojo.getCorpid(),XbbRefTypeEnum.REFUND.getCode());
        }
        List<JSONObject> searchList = chartResultPojo.getSearch();
        if (CollectionsUtil.isNotEmpty(searchList)) {
            for (int i = 0; i < searchList.size(); i++) {
                JSONObject jsonObject = searchList.get(i);
                ConditionsEntityExt condition = JSON.parseObject(jsonObject.toJSONString(), ConditionsEntityExt.class);
                condition.setAttr(ReturnEnum.getAttrConnectData(ReturnEnum.LINK_DISTRIBUTOR));
                ConditionEnum conditionEnum = ConditionEnum.getConditionEnum(condition.getSymbol());
                EsUtil.symbolChoose4Distributor(boolQueryBuilder, conditionEnum, condition);
            }
        }
    }
    /**
     * 从报价单中获取客户
     *
     * @param chartResultPojo 基本参数
     * @param customerQuery 客户查询参数
     * @throws XbbException getFloorAggsKey会产生异常
     * @author 徐俊杰
     * @date 2019/8/26 9:30
     * @since v1.0
     */
    private void getQuotationCustomer(ChartResultPojo chartResultPojo, BoolQueryBuilder customerQuery) throws XbbException {
        BoolQueryBuilder quotationQuery = quotationCountQuery(chartResultPojo);
        //时间聚合字段
        String fieldTime = QuotationEnum.getEsAttr4Keyword(QuotationEnum.QUOTATION_TIME);
        //排重字段
        String fieldGroup = QuotationEnum.getEsAttr4Keyword(QuotationEnum.CUSTOMER_ID);
        FloorAggsKeyPojo floorAggsKeyPojo = systemChartHelp.getFloorAggsKey(chartResultPojo, IndexTypeEnum.IDX_SAAS_QUOTATION, quotationQuery, fieldTime, fieldGroup, true);
        Set totalSet = floorAggsKeyPojo.getTotalSet();
        if (totalSet.size() == 0) {
            totalSet.add(-1L);
        }
        customerQuery.filter(termsQuery("dataId", totalSet));
    }

    /**
     * 封装全景图里面计划收款的QueryBuilder
     *
     * @param panoramaPojo
     * @param isFinish
     * @return
     */
    public BoolQueryBuilder panoramaQuery4PaymentTask(PanoramaPojo panoramaPojo, Integer isFinish) {
        String statusAttr =  PaymentTaskEnum.getEsAttr4Keyword(PaymentTaskEnum.STATUS);
        String userAttr = PaymentTaskEnum.CREATOR_ID.getAttr() + ".keyword";
        String dataAttr = PaymentTaskEnum.getEsAttr4Keyword(PaymentTaskEnum.LINK_CUSTOMER);
        Long dataId = panoramaPojo.getDataId();
        BoolQueryBuilder pamentTaskQueryBuilder = panoramaQuery4Base(panoramaPojo, userAttr, dataAttr, dataId);
        pamentTaskQueryBuilder.filter(termQuery(statusAttr,isFinish));
        //无需审批和通过
        pamentTaskQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return pamentTaskQueryBuilder;
    }

    /**
     * 封装全景图里面应收未收的QueryBuilder
     *
     * @param panoramaPojo
     * @return
     */
    public BoolQueryBuilder panoramaQuery4Payment(PanoramaPojo panoramaPojo) {
        String userAttr = PaymentEnum.CREATOR_ID.getAttr() + ".keyword";
        String dataAttr = PaymentEnum.getEsAttr4Keyword(PaymentEnum.CUSTOMER);
        Long dataId = panoramaPojo.getDataId();
        BoolQueryBuilder pamentTaskQueryBuilder = panoramaQuery4Base(panoramaPojo, userAttr, dataAttr, dataId);
        //无需审批和通过
        pamentTaskQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return pamentTaskQueryBuilder;
    }

    /**
     * 搜索时增加时间限制
     * @param boolQueryBuilder
     * @param checkingDate
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder statementQuery4CheckData(BoolQueryBuilder boolQueryBuilder, List<Long> checkingDate) {
        return chartHelp.statementQuery4CheckData(boolQueryBuilder, checkingDate);
    }

    /**
     * 对账单查询条件封装：corpid、del、checkingDate(开始、结束)
     * @param corpid 公司id
     * @param statementCustomerPojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder statementQuery4CheckData(String corpid, StatementCustomerPojo statementCustomerPojo) {
        return chartHelp.statementQuery4CheckData(corpid, statementCustomerPojo);
    }

    /**
     * 对账单查询条件封装：corpid、del、checkingDate(开始、结束)、客户id
     * @param corpid
     * @param statementCustomerPojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder statementQuery4Table(String corpid, StatementCustomerPojo statementCustomerPojo) {
        //corpid、del、checkingDate(开始、结束)
        BoolQueryBuilder boolQueryBuilder = statementQuery4CheckData(corpid, statementCustomerPojo);
        //限制客户id条件
        setCustomerIdFilter4Statement(statementCustomerPojo, boolQueryBuilder);
        return boolQueryBuilder;
    }

    /**
     * 获取当页之前所有数据的汇总
     * @param corpid
     * @param firstCheckingDate 某页第一条对账记录的checkingDate
     * @param firstCustomerStatementId 某页第一条对账记录的customerStatementId
     * @param statementCustomerPojo
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public BoolQueryBuilder statementQuery4PageFirstBefore(String corpid, Long firstCheckingDate, Long firstCustomerStatementId, StatementCustomerPojo statementCustomerPojo) {
        //corpid,del
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(corpid);
        /*
         checkingDate小于firstSheetTime
         or
         checkingDate与firstSheetTime相等，且对账记录id小于该页第一个firstCustomerStatementId
         */
        BoolQueryBuilder oneQueryBuilder = boolQuery();
        BoolQueryBuilder twoQueryBuilder = boolQuery();
        oneQueryBuilder.filter(rangeQuery(CustomerStatementEnum.getEsAttr(CustomerStatementEnum.CHECKING_DATE)).lt( firstCheckingDate));
        twoQueryBuilder.filter(termQuery(CustomerStatementEnum.getEsAttr(CustomerStatementEnum.CHECKING_DATE), firstCheckingDate));
        twoQueryBuilder.filter(rangeQuery(BusinessConstant.ID).lt(firstCustomerStatementId));
        boolQueryBuilder.should(oneQueryBuilder);
        boolQueryBuilder.should(twoQueryBuilder);
        boolQueryBuilder.minimumShouldMatch(MINI_MUM_SHOULD_MATCH);
        //限制客户id条件
        setCustomerIdFilter4Statement(statementCustomerPojo, boolQueryBuilder);
        return boolQueryBuilder;
    }

    /**
     * 限制对账单统计的客户id条件（单个对账表为一个customerId，导出、打印、总汇总计算为customerIdList）
     * @param statementCustomerPojo
     * @param boolQueryBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void setCustomerIdFilter4Statement(StatementCustomerPojo statementCustomerPojo, BoolQueryBuilder boolQueryBuilder) {
        chartHelp.setCustomerIdFilter4Statement(statementCustomerPojo, boolQueryBuilder);
    }
    // -------------------------------线索相关条件的封装

    /**
     * 线索相关条件封装
     *
     * @param chartResultPojo
     * @param fieldTime
     * @param limitStart
     * @return
     */
    public BoolQueryBuilder clueQuery4Team(ChartResultPojo chartResultPojo, String fieldTime, boolean limitStart) {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Form(chartResultPojo, fieldTime, limitStart);
        getChildTeam(chartResultPojo, boolQueryBuilder);
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 新增线索
     *
     * @param userFlag
     * @param chartResultBasePojo
     * @param fieldTime
     * @return
     */
    public BoolQueryBuilder clueQuery4Create(boolean userFlag, ChartResultPojo chartResultBasePojo, String fieldTime) {
        //新增线索都限制开始时间
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Form(chartResultBasePojo, fieldTime, true);
        //移除del，重新塞入新的条件：新增线索要包含回收站
        esHelper.removeSearchCondition(boolQueryBuilder, "del");
        boolQueryBuilder.filter(termsQuery("del", Arrays.asList(DelEnum.NORMAL.getDel())));
        if (userFlag && chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            //表本身的createId不能变更，因此在data字典内增加创建人key
            //String fieldGroup = ClueEnum.CREATOR_ID.getAttr();
            String fieldGroup = "creatorId.keyword";
            boolQueryBuilder.filter(termsQuery(fieldGroup, chartResultBasePojo.getUserIdIn()));
        }
        //getChildTeam(chartResultBasePojo, boolQueryBuilder);
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        return boolQueryBuilder;
    }

    /**
     * 获取子查询条件(线索专用的)
     *
     * @param chartResultBasePojo
     * @param boolQueryBuilder
     */
    public void getChildTeam(ChartResultPojo chartResultBasePojo, BoolQueryBuilder boolQueryBuilder) {
        // 范围筛选
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_CLUE_TEAM, chartResultBasePojo.getCorpid(), 1, chartResultBasePojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
    }

    /**
     * @param systemCodeEnum
     * @param resultThroughPojo
     * @param resultThroughVo
     * @return
     */
    public BoolQueryBuilder throughQuery4Clue(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) throws XbbException {
        String corpid = resultThroughPojo.getCorpid();
        Long formId = resultThroughPojo.getRefId();
        String dictionaryCode = resultThroughPojo.getDictionaryCode();
        String head = resultThroughPojo.getHead();
        String businessTitle = "";
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        String fieldTime = "addTime";
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        switch (indicatorRuleEnum) {
            case CLUE_ADD:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = clueQuery4Create(true, resultThroughPojo, fieldTime);
                String field = null;
                if (StringUtil.isNotEmpty(dictionaryCode)) {
                    if (!Objects.equals("other", dictionaryCode)) {
                        field = ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_SOURCE);
                        boolQueryBuilder.filter(termQuery(field, dictionaryCode));
                    } else {
                        // 要支持穿透, 我也只能重新查一遍再遍历
                        PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByFormId(formId, corpid);
                        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(paasFormExplainEntity.getExplains());
                        FieldAttrEntity fieldAttrEntity = explainMap.get(ClueEnum.CLUE_SOURCE.getAttr());
                        List<ItemPoJo> items = fieldAttrEntity.getItems();
                        List<String> idIn = new ArrayList<>();
                        items.forEach(item->{
                            idIn.add(String.valueOf(item.getValue()));

                        });
                        field = ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_SOURCE);
                        boolQueryBuilder.filter(boolQuery().mustNot(termsQuery(field,idIn)));
                    }
                }
                break;
            case CLUE_TOTAL:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = clueQuery4Team(resultThroughPojo, fieldTime, false);
                break;
            case CLUE_FOLLOW:
                businessTitle = indicatorRuleEnum.getThroughName();
                //从跟进记录中获取跟进的客户id
                getFollowIdfromCommunicate(XbbRefTypeEnum.CLUE, resultThroughPojo, boolQueryBuilder, true);
                // getChildTeam(resultThroughPojo, boolQueryBuilder);
                break;
            case CLUE_CONVERSION_RATE:
            case CLUE_CONVERSION:
                businessTitle = indicatorRuleEnum.getThroughName();
                if (!Objects.equals(systemCodeEnum, SystemCodeEnum.CLUE_CONVERSION_CHART)) {
                    if (Objects.equals(systemCodeEnum, SystemCodeEnum.CLUE_CONVERSION) || Objects.equals(systemCodeEnum, SystemCodeEnum.CLUE_CONVERSION_RATE)) {
                        boolQueryBuilder = clueQuery4Conversion(false, resultThroughPojo, fieldTime, true);
                    } else {
                        boolQueryBuilder = clueQuery4Conversion(false, resultThroughPojo, fieldTime, false);
                    }
                    getChildTeam(resultThroughPojo, boolQueryBuilder);
                    if (StringUtil.isNotEmpty(dictionaryCode)) {
                        boolQueryBuilder.filter(termQuery(ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_SOURCE), dictionaryCode));
                        boolQueryBuilder.filter(termQuery(ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_STATUS), ClueStatusEnum.TRANSFORMED.getCode()));
                    }
                } else {
                    boolQueryBuilder = clueQuery4ConversionChart(resultThroughPojo, fieldTime);
                    getChildTeam(resultThroughPojo, boolQueryBuilder);
                }
                break;
            case CLUE_STAGE:
                if (formId == null || formId == 0) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, String.format(StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), I18nMessageUtil.getMessage(CommonConstant.FORM)));
                }
                boolQueryBuilder = boolQueryBuilder4Base(corpid);
                if (formId != null && formId > 0) {
                    boolQueryBuilder.filter(termQuery("formId", formId));
                }
                //无需审批和通过
                boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
                getChildTeam(resultThroughPojo, boolQueryBuilder);
                clueFunnelQuery(resultThroughPojo, boolQueryBuilder, true, null);
                field = ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_STAGE);
                boolQueryBuilder.filter(termsQuery(field, dictionaryCode));
                // 穿透名称校验
                List<ItemPoJo> dictionaryList = new ArrayList<>();
                List<ClueStageEntity> list = clueStageModel.getList(corpid,formId, null);
                for(ClueStageEntity stage : list){
                    String stageCode = String.valueOf(stage.getCode());
                    if(Objects.equals(stage.getType(), StageTypeEnum.FAIL_STAGE.getType())){
                        continue;
                    }
                    ItemPoJo itemPoJo = new ItemPoJo();
                    itemPoJo.setText(stage.getName());
                    itemPoJo.setValue(stageCode);
                    dictionaryList.add(itemPoJo);
                }
                String stageTitle = commonHelp.formatThroughDictionary(dictionaryCode, indicatorRuleEnum.getName(), dictionaryList);
                businessTitle = indicatorRuleEnum.getThroughName() +stageTitle;
                break;
            case CLUE_SOURCE:
                break;
            case CLUE_INVALID_RESOURCE:
                businessTitle = indicatorRuleEnum.getThroughName();
                if (formId == null || formId == 0) {
                    throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, String.format(StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), I18nMessageUtil.getMessage(CommonConstant.FORM)));
                }
                boolQueryBuilder = boolQueryBuilder4Base(corpid);
                if (formId != null && formId > 0) {
                    boolQueryBuilder.filter(termQuery("formId", formId));
                }
                //无需审批和通过
                boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
                clueFunnelQuery(resultThroughPojo, boolQueryBuilder, true, null);
                field = ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_STATUS);
                boolQueryBuilder.filter(termQuery(field, ClueStatusEnum.INVALID.getCode()));
                getChildTeam(resultThroughPojo, boolQueryBuilder);
                field = ClueEnum.getEsAttr4Keyword(ClueEnum.INVALID_REASON);
                boolQueryBuilder.filter(termQuery(field, dictionaryCode));
                break;
            case PK:
                businessTitle = indicatorRuleEnum.getThroughName();
                boolQueryBuilder = boolQueryBuilder4Time(resultThroughPojo, fieldTime, true);
                getChildTeam(resultThroughPojo, boolQueryBuilder);
                break;
            default:
                break;

        }
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        resultThroughVo.setHead(head + businessTitle);
        return boolQueryBuilder;
    }

    /**
     * 线索转化漏斗查询条件
     *
     * @param resultThroughPojo
     * @param fieldTime
     * @return
     */
    private BoolQueryBuilder clueQuery4ConversionChart(ResultThroughPojo resultThroughPojo, String fieldTime) throws XbbException {
        String dictionaryCode = resultThroughPojo.getDictionaryCode();
        Long formId = resultThroughPojo.getRefId();
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(resultThroughPojo.getCorpid());
        List<Long> clueIds = new ArrayList<>();
        Boolean flag = false;
        switch (dictionaryCode) {
            case "1":
                clueIds = getClueIds2CLue(resultThroughPojo, formId);
                break;
            case "2":
                clueIds = getClueIds2Customer(resultThroughPojo, formId);
                break;
            case "3":
                clueIds = getClue2Opportunity(resultThroughPojo, formId, flag);
                break;
            case "4":
                flag = true;
                clueIds = getClue2Opportunity(resultThroughPojo, formId, true);
                break;
            case "5":
                clueIds = getClue2Contract(resultThroughPojo, formId);
                break;
            default:
                break;
        }
        getChildTeam(resultThroughPojo, boolQueryBuilder);
        boolQueryBuilder.filter(termsQuery("dataId", clueIds));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        return boolQueryBuilder;
    }

    /**
     * 合同销售线索id
     *
     * @param resultThroughPojo
     * @param formId
     * @return
     * @throws XbbException
     */
    private List<Long> getClue2Contract(ResultThroughPojo resultThroughPojo, Long formId) throws XbbException {
        List<Long> clueIds = getClueIds2CLue(resultThroughPojo, formId);
        List<PaasFormDataEntityExt> clue2Customer = getClue2Customer(resultThroughPojo, clueIds);
        Map<Long, List<Long>> customer2Clue = new HashMap<>(clue2Customer.size());
        List<Long> customerIds = new ArrayList<>();
        clueIds.clear();
        clueIds = new ArrayList<>();
        for (PaasFormDataEntityExt paasFormDataEntityExt : clue2Customer) {
            JSONObject data = paasFormDataEntityExt.getData();
            if (Objects.isNull(data.get(CustomerManagementEnum.CLUE_ID.getAttr()))) {
                continue;
            }
            List<Long> longs = JSONArray.parseArray(data.get(CustomerManagementEnum.CLUE_ID.getAttr()).toString(), Long.class);
            customer2Clue.put(paasFormDataEntityExt.getDataId(), longs);
            customerIds.add(paasFormDataEntityExt.getDataId());
        }
        if (clue2Customer.size() > 0) {
            // 合同
            BoolQueryBuilder boolQueryBuilder = getBaseClueConversion(resultThroughPojo, customerIds, ContractEnum.LINK_CUSTOMER.getAttr());
            String field = ContractEnum.getEsAttr4Keyword(ContractEnum.LINK_CUSTOMER);
            boolQueryBuilder.filter(termsQuery(field, customerIds));
            TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("aggs01").field(field);
            termsAggregationBuilder.size(Integer.MAX_VALUE);
            JSONObject resultJsonObject = esHelper.elasticAggs4Buckets2Object(IndexTypeEnum.IDX_SAAS_CONTRACT, boolQueryBuilder, termsAggregationBuilder);
            JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");
            for (Object o : bucketsArray) {
                JSONObject object = (JSONObject) o;
                Long key = object.getLong("key");
                Integer value = object.getInteger("doc_count");
                if (!Objects.equals(value, BasicConstant.ZERO)) {
                    clueIds.addAll(customer2Clue.get(key));
                }
            }
        }
        return clueIds;
    }

    /**
     * 计算销售机会对应的销售线索id
     *
     * @param resultThroughPojo
     * @param formId
     * @param flag              true 赢单销售机会对应的线索 false 销售机会对应的线索
     * @return
     * @throws XbbException
     */
    private List<Long> getClue2Opportunity(ResultThroughPojo resultThroughPojo, Long formId, Boolean flag) throws XbbException {
        List<Long> clueIds = getClueIds2CLue(resultThroughPojo, formId);
        List<PaasFormDataEntityExt> clue2Customer = getClue2Customer(resultThroughPojo, clueIds);
        Map<Long, List<Long>> customer2Clue = new HashMap<>(clue2Customer.size());
        List<Long> customerIds = new ArrayList<>();
        clueIds.clear();
        for (PaasFormDataEntityExt paasFormDataEntityExt : clue2Customer) {
            JSONObject data = paasFormDataEntityExt.getData();
            if (Objects.isNull(data.get(CustomerManagementEnum.CLUE_ID.getAttr()))) {
                continue;
            }
            List<Long> longs = JSONArray.parseArray(data.get(CustomerManagementEnum.CLUE_ID.getAttr()).toString(), Long.class);
            customer2Clue.put(paasFormDataEntityExt.getDataId(), longs);
            customerIds.add(paasFormDataEntityExt.getDataId());
        }
        if (clue2Customer.size() > 0) {
            // 销售机会
            BoolQueryBuilder boolQueryBuilder = getBaseClueConversion(resultThroughPojo, customerIds, SalesOpportunityEnum.CUSTOMER_NAME.getAttr());
            if (flag) {
                // 【获取成功阶段的阶段id，用termsQuery查询阶段id字段】
//                String saleStage = SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.SALE_STAGE);
//                boolQueryBuilder.filter(termQuery(saleStage, SaleStageEnum.DEAL.getCode()));
                String saleStageId = SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.STAGE_ID);
                List<SaleStageEntity> saleStageList = saleStageModel.getList(resultThroughPojo.getCorpid(), null, null, StageTypeEnum.SUCCESS_STAGE.getType());
                List<Long> saleStageIdList = saleStageList.stream().map(SaleStageEntity::getId).collect(Collectors.toList());
                boolQueryBuilder.filter(termsQuery(saleStageId, saleStageIdList));
            }
            String field = SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.CUSTOMER_NAME);
            boolQueryBuilder.filter(termsQuery(field, customerIds));
            TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("aggs01").field(field);
            termsAggregationBuilder.size(Integer.MAX_VALUE);
            JSONObject resultJsonObject = esHelper.elasticAggs4Buckets2Object(IndexTypeEnum.IDX_SAAS_OPPORTUNITY, boolQueryBuilder, termsAggregationBuilder);
            JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");
            for (Object o : bucketsArray) {
                JSONObject object = (JSONObject) o;
                Long key = object.getLong("key");
                Integer value = object.getInteger("doc_count");
                if (CollectionsUtil.isEmpty(clueIds)) {
                    clueIds = new ArrayList<>();
                }
                if (!Objects.equals(value, BasicConstant.ZERO)) {
                    clueIds.addAll(customer2Clue.get(key));
                }
            }
        }
        return clueIds;
    }

    private List<Long> getClueIds2Customer(ResultThroughPojo resultThroughPojo, Long formId) throws XbbException {
        List<Long> clueIds = getClueIds2CLue(resultThroughPojo, formId);
        List<PaasFormDataEntityExt> clue2Customer = getClue2Customer(resultThroughPojo, clueIds);
        // 记录线索与客户关系
        Map<Long, List<Long>> customer2Clue = new HashMap<>(clue2Customer.size());
        List<Long> res = new ArrayList<>();
        for (PaasFormDataEntityExt paasFormDataEntityExt : clue2Customer) {
            JSONObject data = paasFormDataEntityExt.getData();
            if (Objects.isNull(data.get(CustomerManagementEnum.CLUE_ID.getAttr()))) {
                continue;
            }
            List<Long> longs = JSONArray.parseArray(data.get(CustomerManagementEnum.CLUE_ID.getAttr()).toString(), Long.class);
            if (CollectionsUtil.isEmpty(res)) {
                res = new ArrayList<>();
            }
            longs.retainAll(clueIds);
            res.addAll(longs);
            customer2Clue.put(paasFormDataEntityExt.getDataId(), longs);
        }
        return res;
    }

    /**
     * 获取线索id 线索转化漏斗 第一行穿透
     *
     * @param resultThroughPojo
     * @param formId
     * @return
     * @throws XbbException
     */
    private List<Long> getClueIds2CLue(ResultThroughPojo resultThroughPojo, Long formId) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Base(resultThroughPojo.getCorpid());
        List<String> fieldList;
        if (formId != null && formId > 0) {
            boolQueryBuilder.filter(termQuery("formId", formId));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        fieldList = new ArrayList<>();
        fieldList.add(StringConstant.DATA_ID);
        clueFunnelQuery(resultThroughPojo, boolQueryBuilder, true, null);
        List<PaasFormDataEntityExt> esEntity = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
        List<Long> clueIds = new ArrayList<>();
        esEntity.forEach(item -> {
            clueIds.add(item.getDataId());
        });
        return clueIds;
    }

    /**
     * 线索阶段漏斗统计入参
     *
     * @param chartResultPojo
     * @param boolQueryBuilder
     * @param limitStart
     * @param dictionaryCode
     */
    public BoolQueryBuilder clueFunnelQuery(ChartResultPojo chartResultPojo, BoolQueryBuilder boolQueryBuilder, boolean limitStart, String dictionaryCode) throws XbbException {
        String corpid = chartResultPojo.getCorpid();
        //业务模版筛选
        Long formId = chartResultPojo.getRefId();

        Integer startTime = chartResultPojo.getStartTime();
        Integer endTime = chartResultPojo.getEndTime();
        ChartEntity chartEntity = chartResultPojo.getChartEntity();
        //如果是 创建时间 和  预计结束时间 ,或者最后跟进时间date_2的话 ，则走正常的时间筛选(创建时间addTime筛选前面不要加data.前缀)
        String fieldTime = ClueEnum.ADD_TIME.getAttr();
        RangeQueryBuilder rangeQueryBuilder = rangeQuery(fieldTime);
        if (limitStart) {
            //如果该值为true，则限制开始时间；如果该值为fale，则不限制开始时间。部分特殊场景不限制开始时间，如销售机会区域分布统计等
            rangeQueryBuilder.gte(startTime);
        }
        boolQueryBuilder.filter(rangeQueryBuilder.lt(endTime));
        getChildTeam(chartResultPojo, boolQueryBuilder);
        return boolQueryBuilder;
    }

    /**
     * 已转化线索
     *
     * @param userFlag
     * @param chartResultBasePojo
     * @param fieldTime
     * @param flag                穿透用到 true 加线索阶段 false 不加线索阶段
     * @return
     */
    public BoolQueryBuilder clueQuery4Conversion(boolean userFlag, ChartResultPojo chartResultBasePojo, String fieldTime, Boolean flag) {

        //新增线索都限制开始时间
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4Form(chartResultBasePojo, fieldTime, true);
        //移除del，重新塞入新的条件：新增线索要包含回收站
        esHelper.removeSearchCondition(boolQueryBuilder, "del");
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        //限制创建人id、且非主管或筛选
        // if (userFlag && chartDtoHelp.judgePermissionAndIfCheck(chartResultBasePojo)) {
        //     //表本身的createId不能变更，因此在data字典内增加创建人key
        //     String fieldGroup = ClueEnum.getEsAttr4Keyword(ClueEnum.CREATOR_ID);
        //     boolQueryBuilder.filter(termsQuery(fieldGroup, chartResultBasePojo.getUserIdIn()));
        // }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));

        String clueStage = null;
        if (flag) {
            String esAttr4Keyword = ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_STATUS);
            boolQueryBuilder.filter(termQuery(esAttr4Keyword, ClueStatusEnum.TRANSFORMED.getCode()));
        }
        return boolQueryBuilder;
    }

    /**
     * 獲取线索对应的客户
     *
     * @param chartResultPojo
     * @param clueIds
     * @return
     * @throws XbbException
     */
    public List<PaasFormDataEntityExt> getClue2Customer(ChartResultPojo chartResultPojo, List<Long> clueIds) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termsQuery("data." + CustomerManagementEnum.CLUE_ID.getAttr() + ".keyword", clueIds));
        boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, chartResultPojo.getCorpid()));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        List<String> fieldList = new ArrayList<>();
        fieldList.clear();
        fieldList.add(StringConstant.DATA_ID);
        fieldList.add("data." + CustomerManagementEnum.CLUE_ID.getAttr());
        IndexTypeEnum indexTypeEnum = indexTypeModel.getCustomerIndex(chartResultPojo.getCorpid(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), SaasMarkEnum.SAAS.getCode());

        return esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
    }

    /**
     * 线索转化基礎條件
     *
     * @param chartResultPojo
     * @param customerIds
     * @param attr
     * @return
     */
    public BoolQueryBuilder getBaseClueConversion(ChartResultPojo chartResultPojo, List<Long> customerIds, String attr) {
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        // boolQueryBuilder.filter(termQuery(attr, customerIds));
        boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, chartResultPojo.getCorpid()));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        return boolQueryBuilder;
    }
    /**
     * 获取比例
     *
     */
    /**
     * 获取转化率
     *
     * @param denominator 分母
     * @param numerator   分子
     * @return
     */
    public String getRate(Integer denominator, Integer numerator) {
        String rate = "-";
        if (!Objects.equals(denominator, BasicConstant.ZERO)) {
            rate = Arith.div(numerator * 100d, denominator, 2) + "%";
        }
        return rate;
    }

    public BoolQueryBuilder throughQuery4Market(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo, IndexTypeEnum indexTypeEnum) throws XbbException {
        String dictionaryCode = resultThroughPojo.getDictionaryCode();
        String[] split = dictionaryCode.split("\\+");
        if (split.length != 2) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        String fieldTime = "marketEndTime";
        BoolQueryBuilder returnBoolQuery = new BoolQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(resultThroughPojo, fieldTime);
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        switch (indicatorRuleEnum) {
            case MARKET_ACTIVITY_ROI:
                break;
            case MARKET_ACTIVITY_CONVERSION_ROI:
                returnBoolQuery = getReturnBoolQuery(systemCodeEnum, resultThroughPojo, indexTypeEnum, split, boolQueryBuilder, "marketName.keyword");
                break;
            case MARKET_ACTIVITY_TYPE_CONVERSION_ROI:
                returnBoolQuery = getReturnBoolQuery(systemCodeEnum, resultThroughPojo, indexTypeEnum, split, boolQueryBuilder, "marketType.keyword");
        }
        String businessTitle = getBusinessTitle(split);
        //头部标题，最终需要封装成如此格式：“全公司2019-08-01到2019-08-31新增报价单数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return returnBoolQuery;
    }

    private String getBusinessTitle(String[] split) throws XbbException {
        String businessTitle = "";
        switch (split[1]) {
            case "clueNum":
                //线索数量
                businessTitle = I18nMessageUtil.getMessage(ChartI18Constant.MARKET_ACTIVITY_THROUGH_CLUE_NUM);
                break;
            case "clueInvalid":
                //有效线索数量
                businessTitle = I18nMessageUtil.getMessage(ChartI18Constant.MARKET_ACTIVITY_THROUGH_CLUE_INVALID);
                break;
            case "conversionClue":
                //已转化线索数量
                businessTitle = I18nMessageUtil.getMessage(ChartI18Constant.MARKET_ACTIVITY_THROUGH_CONVERSION_CLUE);
                break;
            case "customer":
                // 客户数
                businessTitle = I18nMessageUtil.getMessage(ChartI18Constant.MARKET_ACTIVITY_THROUGH_CONVERSION_CUSTOMER);
                break;
            case "opportunityTotal":
                //销售机会数量
                businessTitle = I18nMessageUtil.getMessage(ChartI18Constant.MARKET_ACTIVITY_THROUGH_CONVERSION_OPPORTUNITY_TOTAL);
                break;
            case "opportunityWin":
                //赢单机会数
                businessTitle = I18nMessageUtil.getMessage(ChartI18Constant.MARKET_ACTIVITY_THROUGH_CONVERSION_OPPORTUNITY_WIN);
                break;
            case "contract":
                //合同订单数
                businessTitle = I18nMessageUtil.getMessage(ChartI18Constant.MARKET_ACTIVITY_THROUGH_CONVERSION_CONTRACT);
                break;
            default:
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        return businessTitle;
    }

    /**
     * 获取返回的查询条件
     *
     * @param systemCodeEnum
     * @param resultThroughPojo
     * @param indexTypeEnum
     * @param split
     * @param boolQueryBuilder
     * @param s
     * @return
     * @throws XbbException
     */
    public BoolQueryBuilder getReturnBoolQuery(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, IndexTypeEnum indexTypeEnum, String[] split, BoolQueryBuilder boolQueryBuilder, String s) throws XbbException {
        String attr;
        BoolQueryBuilder returnBoolQuery;
        attr = s;
        // 市场活动名称筛选
        boolQueryBuilder.filter(termQuery(attr, split[0]));
        switch (split[1]) {
            case "clueNum":
                returnBoolQuery = throughMarket2ClueTotal(boolQueryBuilder, systemCodeEnum, indexTypeEnum, split, attr, resultThroughPojo);
                break;
            case "clueInvalid":
                returnBoolQuery = throughMarket2ClueValid(boolQueryBuilder, systemCodeEnum, indexTypeEnum, split, attr, resultThroughPojo);
                break;
            case "conversionClue":
                returnBoolQuery = throughMarket2ClueConversion(boolQueryBuilder, systemCodeEnum, indexTypeEnum, split, attr, resultThroughPojo);
                break;
            case "customer":
                returnBoolQuery = throughMarket2Customer(boolQueryBuilder, systemCodeEnum, indexTypeEnum, split, attr, resultThroughPojo);
                break;
            case "opportunityTotal":
                returnBoolQuery = throughMarket2SaleTotal(boolQueryBuilder, systemCodeEnum, indexTypeEnum, split, attr, resultThroughPojo);
                break;
            case "opportunityWin":
                returnBoolQuery = throughMarket2SaleValid(boolQueryBuilder, systemCodeEnum, indexTypeEnum, split, attr, resultThroughPojo);
                break;
            case "contract":
                returnBoolQuery = throughMarket2Contract(boolQueryBuilder, systemCodeEnum, indexTypeEnum, split, attr, resultThroughPojo);
                break;
            default:
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        return returnBoolQuery;
    }

    private BoolQueryBuilder throughMarket2ClueConversion(BoolQueryBuilder boolQueryBuilder, SystemCodeEnum systemCodeEnum, IndexTypeEnum indexTypeEnum, String[] split, String attr, ResultThroughPojo chartResultPojo) throws XbbException {
        // 范围选择
        // 市场活动名称
        boolQueryBuilder.filter(termQuery(attr, split[0]));
        // 和产品确认过,已转化线索数量为: 线索状态为已转化的线索
        // boolQueryBuilder.filter(existsQuery("customerId"));
        boolQueryBuilder.filter(termsQuery("clueStatus", ClueStatusEnum.TRANSFORMED.getCode()));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(boolQuery().must(existsQuery("clueId")));
        List<StaticForMarketActivityEntity> listEntity = esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, StaticForMarketActivityEntity.class, null);
        Set<Long> clueIds = new HashSet<>();
        clueIds.add(-1L);
        listEntity.forEach(item -> {
            if (Objects.nonNull(item.getClueId())) {
                clueIds.add(item.getClueId());
            }
        });
        boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", chartResultPojo.getCorpid()));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_CLUE_TEAM, chartResultPojo.getCorpid(), 1, chartResultPojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        boolQueryBuilder.filter(termsQuery("dataId", clueIds));
        return boolQueryBuilder;

    }

    private BoolQueryBuilder throughMarket2SaleValid(BoolQueryBuilder boolQueryBuilder, SystemCodeEnum systemCodeEnum, IndexTypeEnum indexTypeEnum, String[] split, String attr, ResultThroughPojo chartResultPojo) throws XbbException {
        // 范围选择
        // 市场活动名称
        boolQueryBuilder.filter(termQuery(attr, split[0]));
        // 获取所有的成功阶段
        StageFindStrategy stageFindStrategy = StageFindFactory.getStageFindStrategy(XbbRefTypeEnum.SALES_OPPORTUNITY.getCode());
        Set<String> winStatisticsCodeIn = stageFindStrategy.findStageCodeList(chartResultPojo.getCorpid(), new StageFindPojo(StageTypeEnum.getSuccessType()));
        // 【获取所有成功阶段的code，判断对应的key是否为成功阶段code】
        boolQueryBuilder.filter(termsQuery("saleStage", winStatisticsCodeIn));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(boolQuery().must(existsQuery("saleOpportunityId")));
        List<String> fieldList = new ArrayList<>();
        fieldList.add("saleOpportunityId");
        List<StaticForMarketActivityEntity> listEntity = esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, StaticForMarketActivityEntity.class, fieldList);
        Set<Long> clueIds = new HashSet<>();
        listEntity.forEach(item -> {
            if (Objects.nonNull(item.getSaleOpportunityId())) {
                clueIds.add(item.getSaleOpportunityId());
            }
        });
        boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", chartResultPojo.getCorpid()));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_CLUE_TEAM, chartResultPojo.getCorpid(), 1, chartResultPojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        boolQueryBuilder.filter(termsQuery("dataId", clueIds));
        return boolQueryBuilder;
    }

    private BoolQueryBuilder throughMarket2SaleTotal(BoolQueryBuilder boolQueryBuilder, SystemCodeEnum systemCodeEnum, IndexTypeEnum indexTypeEnum, String[] split, String attr, ResultThroughPojo chartResultPojo) throws XbbException {
        // 范围选择
        // 市场活动名称
        boolQueryBuilder.filter(termQuery(attr, split[0]));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(boolQuery().must(existsQuery("saleOpportunityId")));
        List<String> fieldList = new ArrayList<>();
        fieldList.add("saleOpportunityId");
        List<StaticForMarketActivityEntity> listEntity = esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, StaticForMarketActivityEntity.class, fieldList);
        Set<Long> clueIds = new HashSet<>();
        listEntity.forEach(item -> {
            if (Objects.nonNull(item.getSaleOpportunityId())) {
                clueIds.add(item.getSaleOpportunityId());
            }
        });
        boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", chartResultPojo.getCorpid()));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_OPPORTUNITY_TEAM, chartResultPojo.getCorpid(), 1, chartResultPojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        boolQueryBuilder.filter(termsQuery("dataId", clueIds));
        return boolQueryBuilder;
    }

    /**
     * 有限线索数
     *
     * @param boolQueryBuilder
     * @param systemCodeEnum
     * @param indexTypeEnum
     * @param split
     * @param attr
     * @return
     */
    private BoolQueryBuilder throughMarket2ClueValid(BoolQueryBuilder boolQueryBuilder, SystemCodeEnum systemCodeEnum, IndexTypeEnum indexTypeEnum, String[] split, String attr, ResultThroughPojo chartResultPojo) throws XbbException {
        // 范围选择
        // 市场活动名称
        boolQueryBuilder.filter(termQuery(attr, split[0]));
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("clueStatus.keyword", ClueStatusEnum.INVALID.getCode()));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(boolQuery().must(existsQuery("clueId")));
        List<String> fieldList = new ArrayList<>();
        fieldList.add("clueId");
        List<StaticForMarketActivityEntity> listEntity = esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, StaticForMarketActivityEntity.class, fieldList);
        Set<Long> clueIds = new HashSet<>();
        listEntity.forEach(item -> {
            if (Objects.nonNull(item.getClueId())) {
                clueIds.add(item.getClueId());
            }
        });
        boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", chartResultPojo.getCorpid()));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_CLUE_TEAM, chartResultPojo.getCorpid(), 1, chartResultPojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        boolQueryBuilder.filter(termsQuery("dataId", clueIds));
        return boolQueryBuilder;
    }

    /**
     * 线索数量穿透
     *
     * @param boolQueryBuilder
     * @param systemCodeEnum
     * @param indexTypeEnum
     * @param split
     * @param attr
     * @return
     */
    private BoolQueryBuilder throughMarket2ClueTotal(BoolQueryBuilder boolQueryBuilder, SystemCodeEnum systemCodeEnum, IndexTypeEnum indexTypeEnum, String[] split, String attr, ResultThroughPojo chartResultPojo) throws XbbException {
        // 范围选择
        // 市场活动名称
        boolQueryBuilder.filter(termQuery(attr, split[0]));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(boolQuery().must(existsQuery("clueId")));
        List<String> fieldList = new ArrayList<>();
        fieldList.add("clueId");
        List<StaticForMarketActivityEntity> listEntity = esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, StaticForMarketActivityEntity.class, fieldList);
        Set<Long> clueIds = new HashSet<>();
        clueIds.add(-1L);
        listEntity.forEach(item -> {
            if (Objects.nonNull(item.getClueId())) {
                clueIds.add(item.getClueId());
            }
        });

        // 因为这儿是穿透到客户,所以在这里更新一遍systemCodeEnum,indexTypeEnum
        boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", chartResultPojo.getCorpid()));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_CLUE_TEAM, chartResultPojo.getCorpid(), 1, chartResultPojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        if (clueIds.size() > 0) {
            boolQueryBuilder.filter(termsQuery("dataId", clueIds));
        }
        return boolQueryBuilder;
    }

    /**
     * 市场穿透客户表单
     *
     * @param boolQueryBuilder
     * @param systemCodeEnum
     * @param indexTypeEnum
     */
    private BoolQueryBuilder throughMarket2Customer(BoolQueryBuilder boolQueryBuilder, SystemCodeEnum systemCodeEnum, IndexTypeEnum indexTypeEnum, String[] split, String attr, ResultThroughPojo chartResultPojo) throws XbbException {
        // 范围选择
        // 市场活动名称
        boolQueryBuilder.filter(termQuery(attr, split[0]));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(boolQuery().must(existsQuery("customerId")));
        List<String> fieldList = new ArrayList<>();
        fieldList.add("customerId");
        fieldList.add("marketType");
        List<StaticForMarketActivityEntity> listEntity = esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, StaticForMarketActivityEntity.class, fieldList);
        Set<Long> customerIds = new HashSet<>();
        listEntity.forEach(item -> {
            if (Objects.nonNull(item.getCustomerId())) {
                customerIds.add(item.getCustomerId());
            }
        });
        boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", chartResultPojo.getCorpid()));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            IndexTypeEnum customerTeamIndex = indexTypeModel.getCustomerTeamIndex(chartResultPojo.getCorpid(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), SaasMarkEnum.SAAS.getCode());
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(customerTeamIndex, chartResultPojo.getCorpid(), 1, chartResultPojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        boolQueryBuilder.filter(termsQuery("dataId", customerIds));
        return boolQueryBuilder;
    }
    /**
     * 市场穿透合同表单
     *
     * @param boolQueryBuilder
     * @param systemCodeEnum
     * @param indexTypeEnum
     */
    private BoolQueryBuilder throughMarket2Contract(BoolQueryBuilder boolQueryBuilder, SystemCodeEnum systemCodeEnum, IndexTypeEnum indexTypeEnum, String[] split, String attr, ResultThroughPojo chartResultPojo) throws XbbException {
        // 范围选择
        // 市场活动名称
        boolQueryBuilder.filter(termQuery(attr, split[0]));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(boolQuery().must(existsQuery("contractId")));
        List<String> fieldList = new ArrayList<>();
        fieldList.add("contractId");
        List<StaticForMarketActivityEntity> listEntity = esHelper.findByScroll(indexTypeEnum, boolQueryBuilder, StaticForMarketActivityEntity.class, fieldList);
        Set<Long> contractIds = new HashSet<>();
        listEntity.forEach(item -> {
            if (Objects.nonNull(item.getContractId())) {
                contractIds.add(item.getContractId());
            }
        });
        boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", chartResultPojo.getCorpid()));
        boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_CONTRACT_TEAM, chartResultPojo.getCorpid(), 1, chartResultPojo.getUserIdIn());
            boolQueryBuilder.filter(hasChildQueryBuilder);
        }
        boolQueryBuilder.filter(termsQuery("dataId", contractIds));
        return boolQueryBuilder;
    }

    /**
     * 市场活动确定marketId专用
     *
     * @param chartResultPojo
     * @param corpid
     * @param boolQueryBuilder
     * @throws XbbException
     */
    public PageHelper getMarketId(ChartResultPojo chartResultPojo, String corpid, BoolQueryBuilder boolQueryBuilder, Map<Long, String> marketId2Name) throws XbbException {
        Integer pageSize = chartResultPojo.getPageSize();
        Integer page = chartResultPojo.getPage();
        Long count = 0L;
        Set<Long> marketIds = new HashSet<>();
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            marketIds.add(-1L);
            // 先去查询一遍marketId
            String esAttr4Keyword = MarketActivityEnum.getEsAttr4Keyword(MarketActivityEnum.END_TIME);
            BoolQueryBuilder boolQueryBuilder1 = boolQueryBuilder4Time(chartResultPojo, esAttr4Keyword, true);
            HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_MARKET_ACTIVITY_TEAM, chartResultPojo.getCorpid(), 1, chartResultPojo.getUserIdIn());
            boolQueryBuilder1.filter(hasChildQueryBuilder);
            List<String> fieldList = new ArrayList<>();
            fieldList.add("dataId");
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            sourceBuilder.query(boolQueryBuilder1);
            SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_MARKET_ACTIVITY.getIndex());
            searchRequest.source(sourceBuilder);
            List<SortBuilder> sortBuilderList = Collections.singletonList(new FieldSortBuilder("addTime").order(SortOrder.DESC));
            if (Objects.nonNull(chartResultPojo.getRefId())) {
                if (marketIds.contains(chartResultPojo.getRefId())) {
                    boolQueryBuilder1.filter(termQuery("dataId", chartResultPojo.getRefId()));
                }
            }
            XbbAggregatedPage<PaasFormDataEntity> byPage = esHelper.findByPage(IndexTypeEnum.IDX_SAAS_MARKET_ACTIVITY, boolQueryBuilder1, PaasFormDataEntity.class, null, page, pageSize, sortBuilderList);
            List<PaasFormDataEntity> list = byPage.getContent();
            count = xbbElasticsearchRestTemplate.count(searchRequest, PaasFormDataEntity.class);

            list.forEach(item -> {
                marketId2Name.put(item.getDataId(), item.getData().getString(MarketActivityEnum.NAME.getAttr()));
                marketIds.add(item.getDataId());
            });
        } else {
            if (Objects.nonNull(chartResultPojo.getRefId())) {
                String esAttr4Keyword = MarketActivityEnum.getEsAttr4Keyword(MarketActivityEnum.END_TIME);
                BoolQueryBuilder boolQueryBuilder1 = boolQueryBuilder4Time(chartResultPojo, esAttr4Keyword, true);
                HasChildQueryBuilder hasChildQueryBuilder = esHelper.teamQueryBuilder(IndexTypeEnum.IDX_SAAS_MARKET_ACTIVITY_TEAM, chartResultPojo.getCorpid(), 1, chartResultPojo.getUserIdIn());
                boolQueryBuilder1.filter(termQuery("dataId", chartResultPojo.getRefId()));
                boolQueryBuilder1.filter(hasChildQueryBuilder);
                List<String> fieldList = new ArrayList<>();
                fieldList.add("dataId");
                SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
                sourceBuilder.query(boolQueryBuilder1);
                SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_MARKET_ACTIVITY.getIndex());
                searchRequest.source(sourceBuilder);
                List<SortBuilder> sortBuilderList = Collections.singletonList(new FieldSortBuilder("addTime").order(SortOrder.DESC));
                if (Objects.nonNull(chartResultPojo.getRefId())) {
                    if (marketIds.contains(chartResultPojo.getRefId())) {
                        boolQueryBuilder1.filter(termQuery("dataId", chartResultPojo.getRefId()));
                    }
                }
                XbbAggregatedPage<PaasFormDataEntity> byPage = esHelper.findByPage(IndexTypeEnum.IDX_SAAS_MARKET_ACTIVITY, boolQueryBuilder1, PaasFormDataEntity.class, null, page, pageSize, sortBuilderList);
                byPage.getContent().forEach(item->{
                    marketId2Name.put(item.getDataId(), item.getData().getString(MarketActivityEnum.NAME.getAttr()));
                });
                marketIds.add(chartResultPojo.getRefId());
                count = 1L;
            }
        }
        if (marketIds.size() > 0) {
            if (Objects.nonNull(chartResultPojo.getRefId())) {
                if (marketIds.contains(chartResultPojo.getRefId())) {
                    boolQueryBuilder.filter(termQuery("marketId", chartResultPojo.getRefId()));
                } else {
                    boolQueryBuilder.filter(termQuery("marketId", -1L));
                }
            } else {
                boolQueryBuilder.filter(termsQuery("marketId", marketIds));
            }
        } else {
            String esAttr4Keyword = MarketActivityEnum.getEsAttr4Keyword(MarketActivityEnum.END_TIME);
            BoolQueryBuilder boolQueryBuilder2 = boolQueryBuilder4Time(chartResultPojo, esAttr4Keyword, true);
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            sourceBuilder.query(boolQueryBuilder2);
            PageRequest pageRequest = EsUtil.setPage(sourceBuilder, page, pageSize);
            SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_MARKET_ACTIVITY.getIndex());
            searchRequest.source(sourceBuilder);
            List<SortBuilder> sortBuilderList = Collections.singletonList(new FieldSortBuilder("addTime").order(SortOrder.DESC));
            XbbAggregatedPage<PaasFormDataEntity> byPage = esHelper.findByPage(IndexTypeEnum.IDX_SAAS_MARKET_ACTIVITY, boolQueryBuilder2, PaasFormDataEntity.class, null, page, pageSize, sortBuilderList);
            List<PaasFormDataEntity> content = byPage.getContent();
            List<PaasFormDataEntity> list = byPage.getContent();
            count = xbbElasticsearchRestTemplate.count(searchRequest, PaasFormDataEntity.class);

            list.forEach(item -> {
                marketId2Name.put(item.getDataId(), item.getData().getString(MarketActivityEnum.NAME.getAttr()));
                marketIds.add(item.getDataId());
            });
            boolQueryBuilder.filter(termsQuery("marketId", marketIds));
        }

        PageHelper pageHelper = PageHelperUtil.initPageHelper(count.intValue(), page, pageSize, PageConstant.DEFAULT_PAGE_SIZE_SMALL);
        return pageHelper;
    }

    /**
     * 其他收入统计查询条件封装
     *
     * @param chartResultPojo
     * @param typeList
     * @return
     */
    public BoolQueryBuilder otherIncomeQuery4Belong(ChartResultPojo chartResultPojo, List<String> typeList) {
        //时间筛选
        String fieldTime = OtherIncomeEnum.getEsAttr4Keyword(OtherIncomeEnum.INCOME_TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, fieldTime);
        //处理类型
        String typeAttr = OtherIncomeEnum.getEsAttr4Keyword(OtherIncomeEnum.SHEET_TYPE_OTHER);
        if (CollectionsUtil.isNotEmpty(typeList)) {
            boolQueryBuilder.filter(termsQuery(typeAttr, typeList));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            boolQueryBuilder.filter(termsQuery(OtherIncomeEnum.getEsAttr4Keyword(OtherIncomeEnum.BELONG_ID), chartResultPojo.getUserIdIn()));
        }
        return boolQueryBuilder;
    }

    /**
     * 封装查询条件
     *
     * @param chartResultPojo
     * @param typeList
     * @return
     */
    public BoolQueryBuilder otherExpenseQuery4Belong(ChartResultPojo chartResultPojo, List<String> typeList) {
        //时间筛选
        String fieldTime = OtherExpenseEnum.getEsAttr4Keyword(OtherExpenseEnum.EXPENSE_TIME);
        BoolQueryBuilder boolQueryBuilder = boolQueryBuilder4LimitStart(chartResultPojo, fieldTime);
        if (CollectionsUtil.isNotEmpty(typeList)) {
            //处理类型
            String typeAttr = OtherExpenseEnum.getEsAttr4Keyword(OtherExpenseEnum.SHEET_TYPE_OTHER);
            boolQueryBuilder.filter(termsQuery(typeAttr, typeList));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        if (chartDtoHelp.judgePermissionAndIfCheck(chartResultPojo)) {
            boolQueryBuilder.filter(termsQuery(OtherExpenseEnum.getEsAttr4Keyword(OtherExpenseEnum.BELONG_ID), chartResultPojo.getUserIdIn()));
        }
        return boolQueryBuilder;
    }

    /**
     * 其他收入统计
     *
     * @param systemCodeEnum
     * @param resultThroughPojo
     * @param resultThroughVo
     * @return
     */
    public BoolQueryBuilder throughQuery4OtherIncome(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) {
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        String businessTitle = indicatorRuleEnum.getThroughName();
        //其他收入统计
        List<String> types = new ArrayList<>();
        if (Objects.equals(systemCodeEnum, SystemCodeEnum.OTHER_INCOME_RED_AMOUNT_CHART)) {
            types = OtherIncomeSheetTypeEnum.getRedCodeList();
        }else {
            types = OtherIncomeSheetTypeEnum.getAllCodeList();
        }
        BoolQueryBuilder boolQueryBuilder = otherIncomeQuery4Belong(resultThroughPojo, types);
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

    /**
     * 其他支出统计
     *
     * @param systemCodeEnum
     * @param resultThroughPojo
     * @param resultThroughVo
     * @return
     */
    public BoolQueryBuilder throughQuery4OtherExpense(SystemCodeEnum systemCodeEnum, ResultThroughPojo resultThroughPojo, ResultThroughVO resultThroughVo) {
        IndicatorRuleEnum indicatorRuleEnum = systemCodeEnum.getIndicatorRuleEnum();
        String businessTitle = indicatorRuleEnum.getThroughName();
        //其他收入统计
        List<String> types = new ArrayList<>();
        if (Objects.equals(systemCodeEnum, SystemCodeEnum.OTHER_EXPENSE_RED_AMOUNT_CHART)) {
            types = OtherExpenseSheetTypeEnum.getRedCodeList();
        }else {
            types = OtherExpenseSheetTypeEnum.getAllCodeList();
        }
        BoolQueryBuilder boolQueryBuilder = otherExpenseQuery4Belong(resultThroughPojo, types);
        //头部标题，最终需要封装成如此格式：“张三截至2019-12-31客户总数”
        String head = resultThroughPojo.getHead() + businessTitle;
        resultThroughVo.setHead(head);
        return boolQueryBuilder;
    }

}
